mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-2877: Refactoring Loader and AutoImporter to better accommodate loading more than one thing
This commit is contained in:
parent
65e2c720b4
commit
1574262722
40 changed files with 2049 additions and 1300 deletions
|
@ -22,11 +22,20 @@
|
||||||
// list.
|
// list.
|
||||||
//
|
//
|
||||||
//@category Symbol
|
//@category Symbol
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.opinion.ElfLoader;
|
import ghidra.app.util.opinion.ElfLoader;
|
||||||
|
import ghidra.app.util.opinion.Loaded;
|
||||||
|
import ghidra.framework.model.*;
|
||||||
|
import ghidra.program.model.listing.Library;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.ELFExternalSymbolResolver;
|
import ghidra.program.util.ELFExternalSymbolResolver;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.VersionException;
|
||||||
|
|
||||||
public class FixupELFExternalSymbolsScript extends GhidraScript {
|
public class FixupELFExternalSymbolsScript extends GhidraScript {
|
||||||
|
|
||||||
|
@ -38,10 +47,62 @@ public class FixupELFExternalSymbolsScript extends GhidraScript {
|
||||||
")");
|
")");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MessageLog msgLog = new MessageLog();
|
MessageLog messageLog = new MessageLog();
|
||||||
ELFExternalSymbolResolver.fixUnresolvedExternalSymbols(currentProgram, false, msgLog,
|
Object consumer = new Object();
|
||||||
monitor);
|
ProjectData projectData = currentProgram.getDomainFile().getParent().getProjectData();
|
||||||
Msg.info(this, msgLog.toString());
|
List<Loaded<Program>> loadedPrograms = new ArrayList<>();
|
||||||
|
|
||||||
|
// Add current program to list
|
||||||
|
loadedPrograms.add(new Loaded<>(currentProgram, currentProgram.getName(),
|
||||||
|
currentProgram.getDomainFile().getPathname()));
|
||||||
|
|
||||||
|
// Add external libraries to list
|
||||||
|
for (Library extLibrary : ELFExternalSymbolResolver.getLibrarySearchList(currentProgram)) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
String libName = extLibrary.getName();
|
||||||
|
String libPath = extLibrary.getAssociatedProgramPath();
|
||||||
|
if (libPath == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DomainFile libDomainFile = projectData.getFile(libPath);
|
||||||
|
if (libDomainFile == null) {
|
||||||
|
messageLog.appendMsg("Referenced external program not found: " + libPath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DomainObject libDomainObject = null;
|
||||||
|
try {
|
||||||
|
libDomainObject =
|
||||||
|
libDomainFile.getDomainObject(consumer, false, false, monitor);
|
||||||
|
if (libDomainObject instanceof Program program) {
|
||||||
|
loadedPrograms.add(new Loaded<>(program, libName, libPath));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
messageLog
|
||||||
|
.appendMsg("Referenced external program is not a program: " + libPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// failed to open library
|
||||||
|
messageLog.appendMsg("Failed to open library dependency project file: " +
|
||||||
|
libDomainFile.getPathname());
|
||||||
|
}
|
||||||
|
catch (VersionException e) {
|
||||||
|
messageLog.appendMsg(
|
||||||
|
"Referenced external program requires updgrade, unable to consider symbols: " +
|
||||||
|
libPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve symbols
|
||||||
|
ELFExternalSymbolResolver.fixUnresolvedExternalSymbols(loadedPrograms, messageLog, monitor);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
for (int i = 1; i < loadedPrograms.size(); i++) {
|
||||||
|
loadedPrograms.get(i).release(consumer);
|
||||||
|
}
|
||||||
|
Msg.info(this, messageLog.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -17,15 +16,17 @@
|
||||||
//Imports all programs from a selected directory.
|
//Imports all programs from a selected directory.
|
||||||
//@category Import
|
//@category Import
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
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.AutoImporter;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
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;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class ImportAllProgramsFromADirectoryScript extends GhidraScript {
|
public class ImportAllProgramsFromADirectoryScript extends GhidraScript {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -52,36 +53,23 @@ public class ImportAllProgramsFromADirectoryScript extends GhidraScript {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Program program = null;
|
LoadResults<Program> loadResults = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
program = importFile(file);
|
loadResults = AutoImporter.importByLookingForLcs(file, state.getProject(),
|
||||||
|
folder.getPathname(), language.getLanguage(), language.getCompilerSpec(), this,
|
||||||
|
log, monitor);
|
||||||
|
loadResults.getPrimary().save(state.getProject(), log, monitor);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (IOException e) {
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (program == null) {
|
|
||||||
try {
|
|
||||||
program =
|
|
||||||
AutoImporter.importByLookingForLcs(file, folder, language.getLanguage(),
|
|
||||||
language.getCompilerSpec(), this, log, monitor);
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (program == null) {
|
|
||||||
println("Unable to import program from file " + file.getName());
|
println("Unable to import program from file " + file.getName());
|
||||||
}
|
}
|
||||||
else {
|
finally {
|
||||||
//openProgram( program );
|
if (loadResults != null) {
|
||||||
program.release(this);
|
loadResults.release(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println(log.toString());
|
println(log.toString());
|
||||||
|
|
||||||
log.clear();
|
log.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ 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.AutoImporter;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
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;
|
||||||
import ghidra.app.util.viewer.field.CommentUtils;
|
import ghidra.app.util.viewer.field.CommentUtils;
|
||||||
|
@ -3382,18 +3383,38 @@ 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.
|
* null is returned. For more control over the import process, {@link AutoImporter} may be
|
||||||
|
* directly called.
|
||||||
|
* <p>
|
||||||
|
* NOTE: The returned {@link Program} is not automatically saved into the current project.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* needed, where <code>consumer</code> is <code>this</code>.
|
||||||
*
|
*
|
||||||
* @param file the file to import
|
* @param file the file to import
|
||||||
* @return the newly imported program, or null
|
* @return the newly imported program, or null
|
||||||
* @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 {
|
||||||
return AutoImporter.importByUsingBestGuess(file, null, this, new MessageLog(), monitor);
|
try {
|
||||||
|
LoadResults<Program> loadResults = AutoImporter.importByUsingBestGuess(file,
|
||||||
|
state.getProject(), null, this, new MessageLog(), monitor);
|
||||||
|
loadResults.releaseNonPrimary(this);
|
||||||
|
return loadResults.getPrimaryDomainObject();
|
||||||
|
}
|
||||||
|
catch (LoadException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports the specified file as raw binary.
|
* Imports the specified file as raw binary. For more control over the import process,
|
||||||
|
* {@link AutoImporter} may be directly called.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* needed, where <code>consumer</code> is <code>this</code>.
|
||||||
*
|
*
|
||||||
* @param file the file to import
|
* @param file the file to import
|
||||||
* @param language the language of the new program
|
* @param language the language of the new program
|
||||||
|
@ -3403,8 +3424,14 @@ 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 {
|
||||||
return AutoImporter.importAsBinary(file, null, language, compilerSpec, this,
|
try {
|
||||||
new MessageLog(), monitor);
|
Loaded<Program> loaded = AutoImporter.importAsBinary(file, state.getProject(), null,
|
||||||
|
language, compilerSpec, this, new MessageLog(), monitor);
|
||||||
|
return loaded.getDomainObject();
|
||||||
|
}
|
||||||
|
catch (LoadException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,8 +21,7 @@ import java.util.StringTokenizer;
|
||||||
|
|
||||||
import ghidra.app.util.importer.AutoImporter;
|
import ghidra.app.util.importer.AutoImporter;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.opinion.Loader;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.app.util.opinion.PeLoader;
|
|
||||||
import ghidra.framework.main.AppInfo;
|
import ghidra.framework.main.AppInfo;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
@ -128,21 +127,25 @@ public abstract class ProgramCoordinator {
|
||||||
MessageLog messageLog = new MessageLog();
|
MessageLog messageLog = new MessageLog();
|
||||||
DomainFolder folder = getFolder(file.getParent());
|
DomainFolder folder = getFolder(file.getParent());
|
||||||
Class<? extends Loader> loaderClass = PeLoader.class;
|
Class<? extends Loader> loaderClass = PeLoader.class;
|
||||||
|
LoadResults<Program> loadResults = null;
|
||||||
try {
|
try {
|
||||||
Language language = languageService.getDefaultLanguage(
|
Language language = languageService.getDefaultLanguage(
|
||||||
Processor.findOrPossiblyCreateProcessor("x86"));
|
Processor.findOrPossiblyCreateProcessor("x86"));
|
||||||
CompilerSpec compilerSpec =
|
CompilerSpec compilerSpec =
|
||||||
language.getCompilerSpecByID(new CompilerSpecID("windows"));
|
language.getCompilerSpecByID(new CompilerSpecID("windows"));
|
||||||
importProgram = AutoImporter.importByUsingSpecificLoaderClassAndLcs(file,
|
loadResults = AutoImporter.importByUsingSpecificLoaderClassAndLcs(file,
|
||||||
folder, loaderClass, null, language, compilerSpec, consumer, messageLog,
|
AppInfo.getActiveProject(), folder.getPathname(), loaderClass, null,
|
||||||
monitor);
|
language, compilerSpec, consumer, messageLog, monitor);
|
||||||
|
importProgram = loadResults.getPrimaryDomainObject();
|
||||||
programManager.openProgram(importProgram);
|
programManager.openProgram(importProgram);
|
||||||
importProgram.release(this);
|
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
e.printStackTrace();//TODO
|
e.printStackTrace();//TODO
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
if (loadResults != null) {
|
||||||
|
loadResults.release(this);
|
||||||
|
}
|
||||||
importSemaphore.notify();
|
importSemaphore.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,6 +190,14 @@ public class Option {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (Integer.class.isAssignableFrom(getValueClass())) {
|
||||||
|
try {
|
||||||
|
setValue(Integer.decode(str));
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (Address.class.isAssignableFrom(getValueClass())) {
|
else if (Address.class.isAssignableFrom(getValueClass())) {
|
||||||
try {
|
try {
|
||||||
Address origAddr = (Address) getValue();
|
Address origAddr = (Address) getValue();
|
||||||
|
|
|
@ -33,10 +33,12 @@ import docking.widgets.checkbox.GCheckBox;
|
||||||
import docking.widgets.combobox.GComboBox;
|
import docking.widgets.combobox.GComboBox;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
import ghidra.app.util.opinion.AbstractLibrarySupportLoader;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.app.util.opinion.LibraryPathsDialog;
|
import ghidra.framework.main.AppInfo;
|
||||||
import ghidra.framework.main.DataTreeDialog;
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
import ghidra.framework.model.DomainFolder;
|
import ghidra.framework.model.DomainFolder;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.layout.*;
|
import ghidra.util.layout.*;
|
||||||
|
@ -251,21 +253,35 @@ public class OptionsEditorPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component buildProjectFolderEditor(Option option) {
|
private Component buildProjectFolderEditor(Option option) {
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
Project project = AppInfo.getActiveProject();
|
||||||
JTextField textField = new JTextField();
|
final SaveState state;
|
||||||
|
SaveState existingState = project.getSaveableData(Loader.OPTIONS_PROJECT_SAVE_STATE_KEY);
|
||||||
|
if (existingState != null) {
|
||||||
|
state = existingState;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
state = new SaveState();
|
||||||
|
project.setSaveableData(Loader.OPTIONS_PROJECT_SAVE_STATE_KEY, state);
|
||||||
|
}
|
||||||
|
String lastFolderPath = state.getString(option.getName(), "");
|
||||||
|
option.setValue(lastFolderPath);
|
||||||
|
JTextField textField = new JTextField(lastFolderPath);
|
||||||
textField.setEditable(false);
|
textField.setEditable(false);
|
||||||
JButton button = new BrowseButton();
|
JButton button = new BrowseButton();
|
||||||
button.addActionListener(e -> {
|
button.addActionListener(e -> {
|
||||||
DataTreeDialog dataTreeDialog =
|
DataTreeDialog dataTreeDialog =
|
||||||
new DataTreeDialog(this, "Choose a project folder", DataTreeDialog.CHOOSE_FOLDER);
|
new DataTreeDialog(this, "Choose a project folder", DataTreeDialog.CHOOSE_FOLDER);
|
||||||
dataTreeDialog.setSelectedFolder(null);
|
dataTreeDialog.setSelectedFolder(project.getProjectData().getFolder(lastFolderPath));
|
||||||
dataTreeDialog.showComponent();
|
dataTreeDialog.showComponent();
|
||||||
DomainFolder folder = dataTreeDialog.getDomainFolder();
|
DomainFolder folder = dataTreeDialog.getDomainFolder();
|
||||||
if (folder != null) {
|
if (folder != null) {
|
||||||
textField.setText(folder.getPathname());
|
String newFolderPath = folder.getPathname();
|
||||||
option.setValue(folder.getPathname());
|
textField.setText(newFolderPath);
|
||||||
|
option.setValue(newFolderPath);
|
||||||
|
state.putString(option.getName(), newFolderPath);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
panel.add(textField, BorderLayout.CENTER);
|
panel.add(textField, BorderLayout.CENTER);
|
||||||
panel.add(button, BorderLayout.EAST);
|
panel.add(button, BorderLayout.EAST);
|
||||||
return panel;
|
return panel;
|
||||||
|
|
|
@ -31,7 +31,7 @@ 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.AutoImporter;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.opinion.BinaryLoader;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.framework.*;
|
import ghidra.framework.*;
|
||||||
import ghidra.framework.client.ClientUtil;
|
import ghidra.framework.client.ClientUtil;
|
||||||
import ghidra.framework.client.RepositoryAdapter;
|
import ghidra.framework.client.RepositoryAdapter;
|
||||||
|
@ -1382,7 +1382,15 @@ public class HeadlessAnalyzer {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkOverwrite(DomainFile df) throws IOException {
|
private boolean checkOverwrite(Loaded<Program> loaded) throws IOException {
|
||||||
|
DomainFolder folder = project.getProjectData().getFolder(loaded.getProjectFolderPath());
|
||||||
|
if (folder == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
DomainFile df = folder.getFile(loaded.getName());
|
||||||
|
if (df == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (options.overwrite) {
|
if (options.overwrite) {
|
||||||
try {
|
try {
|
||||||
if (df.isHijacked()) {
|
if (df.isHijacked()) {
|
||||||
|
@ -1500,192 +1508,148 @@ public class HeadlessAnalyzer {
|
||||||
|
|
||||||
Msg.info(this, "IMPORTING: " + file.getAbsolutePath());
|
Msg.info(this, "IMPORTING: " + file.getAbsolutePath());
|
||||||
|
|
||||||
Program program = null;
|
LoadResults<Program> loadResults = null;
|
||||||
|
Loaded<Program> primary = null;
|
||||||
try {
|
try {
|
||||||
String dfName = null;
|
|
||||||
DomainFile df = null;
|
|
||||||
DomainFolder domainFolder = null;
|
|
||||||
try {
|
|
||||||
// Gets parent folder for import (creates path if doesn't exist)
|
|
||||||
domainFolder = getDomainFolder(folderPath, false);
|
|
||||||
|
|
||||||
dfName = file.getName();
|
// Perform the load. Note that loading 1 file may result in more than 1 thing getting
|
||||||
|
// loaded.
|
||||||
|
loadResults = loadPrograms(file, folderPath);
|
||||||
|
Msg.info(this, "IMPORTING: Loaded " + (loadResults.size() - 1) + " additional files");
|
||||||
|
|
||||||
if (dfName.toLowerCase().endsWith(".gzf") ||
|
primary = loadResults.getPrimary();
|
||||||
dfName.toLowerCase().endsWith(".xml")) {
|
Program primaryProgram = primary.getDomainObject();
|
||||||
// Use filename without .gzf
|
|
||||||
int index = dfName.lastIndexOf('.');
|
|
||||||
dfName = dfName.substring(0, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Make sure we are allowed to save ALL programs to the project. If not, save none and
|
||||||
|
// fail.
|
||||||
if (!options.readOnly) {
|
if (!options.readOnly) {
|
||||||
if (domainFolder != null) {
|
for (Loaded<Program> loaded : loadResults) {
|
||||||
df = domainFolder.getFile(dfName);
|
if (!checkOverwrite(loaded)) {
|
||||||
}
|
|
||||||
if (df != null && !checkOverwrite(df)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
df = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
program = loadProgram(file);
|
|
||||||
if (program == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if there are defined memory blocks; abort if not (there is nothing
|
|
||||||
// to work with!)
|
|
||||||
if (program.getMemory().getAllInitializedAddressSet().isEmpty()) {
|
|
||||||
Msg.error(this, "REPORT: Error: No memory blocks were defined for file '" +
|
|
||||||
file.getAbsolutePath() + "'.");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exc) {
|
}
|
||||||
Msg.error(this, "REPORT: " + exc.getMessage(), exc);
|
|
||||||
exc.printStackTrace();
|
// Check if there are defined memory blocks in the primary program.
|
||||||
|
// Abort if not (there is nothing to work with!).
|
||||||
|
if (primaryProgram.getMemory().getAllInitializedAddressSet().isEmpty()) {
|
||||||
|
Msg.error(this, "REPORT: Error: No memory blocks were defined for file " +
|
||||||
|
file.getAbsolutePath());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Msg.info(this,
|
// Analyze the primary program, and determine if we should save.
|
||||||
"REPORT: Import succeeded with language \"" +
|
// TODO: Analyze non-primary programs (GP-2965).
|
||||||
program.getLanguageID().getIdAsString() + "\" and cspec \"" +
|
boolean doSave =
|
||||||
program.getCompilerSpec().getCompilerSpecID().getIdAsString() +
|
analyzeProgram(file.getAbsolutePath(), primaryProgram) && !options.readOnly;
|
||||||
"\" for file: " + file.getAbsolutePath());
|
|
||||||
|
|
||||||
boolean doSave;
|
|
||||||
try {
|
|
||||||
|
|
||||||
doSave = analyzeProgram(file.getAbsolutePath(), program) && !options.readOnly;
|
|
||||||
|
|
||||||
if (!doSave) {
|
|
||||||
program.setTemporary(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 program changes.
|
// us to discard any changes
|
||||||
if (program.isTemporary()) {
|
if (!doSave) {
|
||||||
if (options.readOnly) {
|
loadResults.forEach(e -> e.getDomainObject().setTemporary(true));
|
||||||
Msg.info(this, "REPORT: Discarded file import due to readOnly option: " +
|
|
||||||
file.getAbsolutePath());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Msg.info(this, "REPORT: Discarded file import as a result of script " +
|
|
||||||
"activity or analysis timeout: " + file.getAbsolutePath());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// Apply saveDomainFolder to the primary program, if applicable.
|
||||||
|
// We don't support changing the save folder on any non-primary loaded programs.
|
||||||
|
// Note that saveDomainFolder is set by pre/post-scripts, so it can only be used
|
||||||
|
// after analysis happens.
|
||||||
if (saveDomainFolder != null) {
|
if (saveDomainFolder != null) {
|
||||||
|
primary.setProjectFolderPath(saveDomainFolder.getPathname());
|
||||||
df = saveDomainFolder.getFile(dfName);
|
if (!checkOverwrite(primary)) {
|
||||||
|
|
||||||
// Return if file already exists and overwrite == false
|
|
||||||
if (df != null && !checkOverwrite(df)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
domainFolder = saveDomainFolder;
|
|
||||||
}
|
}
|
||||||
else if (domainFolder == null) {
|
|
||||||
domainFolder = getDomainFolder(folderPath, true);
|
|
||||||
}
|
|
||||||
df = domainFolder.createFile(dfName, program, TaskMonitor.DUMMY);
|
|
||||||
Msg.info(this, "REPORT: Save succeeded for file: " + df.getPathname());
|
|
||||||
|
|
||||||
if (options.commit) {
|
// Save
|
||||||
|
for (Loaded<Program> loaded : loadResults) {
|
||||||
AutoAnalysisManager.getAnalysisManager(program).dispose();
|
if (!loaded.getDomainObject().isTemporary()) {
|
||||||
program.release(this);
|
try {
|
||||||
program = null;
|
DomainFile domainFile =
|
||||||
|
loaded.save(project, new MessageLog(), TaskMonitor.DUMMY);
|
||||||
commitProgram(df);
|
Msg.info(this, String.format("REPORT: Save succeeded for: %s (%s)", loaded,
|
||||||
}
|
domainFile));
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
e.printStackTrace();
|
Msg.info(this, "REPORT: Save failed for: " + loaded);
|
||||||
throw new IOException("Cannot create file: " + domainFolder.getPathname() +
|
|
||||||
DomainFolder.SEPARATOR + dfName, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exc) {
|
else {
|
||||||
String logErrorMsg =
|
if (options.readOnly) {
|
||||||
file.getAbsolutePath() + " Error during analysis: " + exc.getMessage();
|
Msg.info(this, "REPORT: Discarded file import due to readOnly option: " +
|
||||||
Msg.info(this, logErrorMsg);
|
loaded);
|
||||||
return false;
|
}
|
||||||
|
else {
|
||||||
|
Msg.info(this,
|
||||||
|
"REPORT: Discarded file import as a result of script " +
|
||||||
|
"activity or analysis timeout: " + loaded);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
if (program != null) {
|
|
||||||
AutoAnalysisManager.getAnalysisManager(program).dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Commit changes
|
||||||
|
if (options.commit) {
|
||||||
|
for (Loaded<Program> loaded : loadResults) {
|
||||||
|
if (!loaded.getDomainObject().isTemporary()) {
|
||||||
|
if (loaded == primary) {
|
||||||
|
AutoAnalysisManager.getAnalysisManager(primaryProgram).dispose();
|
||||||
|
}
|
||||||
|
loaded.release(this);
|
||||||
|
commitProgram(loaded.getSavedDomainFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Msg.info(this, "REPORT: Import succeeded");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
finally {
|
catch (LoadException e) {
|
||||||
// Program must be released here, since the AutoAnalysisManager uses program to
|
|
||||||
// call dispose() in the finally() block above.
|
|
||||||
if (program != null) {
|
|
||||||
program.release(this);
|
|
||||||
program = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Program loadProgram(File file) throws VersionException, InvalidNameException,
|
|
||||||
DuplicateNameException, CancelledException, IOException {
|
|
||||||
|
|
||||||
MessageLog messageLog = new MessageLog();
|
|
||||||
Program program = null;
|
|
||||||
|
|
||||||
// NOTE: we must pass a null DomainFolder to the AutoImporter so as not to
|
|
||||||
// allow the DomainFile to be saved at this point. DomainFile should be
|
|
||||||
// saved after all applicable analysis/scripts are run.
|
|
||||||
|
|
||||||
if (options.loaderClass == null) {
|
|
||||||
// User did not specify a loader
|
|
||||||
if (options.language == null) {
|
|
||||||
program = AutoImporter.importByUsingBestGuess(file, null, this, messageLog,
|
|
||||||
TaskMonitor.DUMMY);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
program = AutoImporter.importByLookingForLcs(file, null, options.language,
|
|
||||||
options.compilerSpec, this, messageLog, TaskMonitor.DUMMY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// User specified a loader
|
|
||||||
if (options.language == null) {
|
|
||||||
program = AutoImporter.importByUsingSpecificLoaderClass(file, null,
|
|
||||||
options.loaderClass, options.loaderArgs, this, messageLog, TaskMonitor.DUMMY);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
program = AutoImporter.importByUsingSpecificLoaderClassAndLcs(file, null,
|
|
||||||
options.loaderClass, options.loaderArgs, options.language, options.compilerSpec,
|
|
||||||
this, messageLog, TaskMonitor.DUMMY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (program == null) {
|
|
||||||
Msg.error(this, "The AutoImporter could not successfully load " +
|
Msg.error(this, "The AutoImporter could not successfully load " +
|
||||||
file.getAbsolutePath() +
|
file.getAbsolutePath() +
|
||||||
" 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.");
|
||||||
|
|
||||||
if (options.loaderClass != null && options.loaderClass != BinaryLoader.class) {
|
if (options.loaderClass != null && options.loaderClass != BinaryLoader.class) {
|
||||||
Msg.error(this,
|
Msg.error(this,
|
||||||
"NOTE: Import failure may be due to missing opinion for \"" +
|
"NOTE: Import failure may be due to missing opinion for \"" +
|
||||||
options.loaderClass.getSimpleName() +
|
options.loaderClass.getSimpleName() +
|
||||||
"\". If so, please contact Ghidra team for assistance.");
|
"\". If so, please contact Ghidra team for assistance.");
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
return null;
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Msg.error(this, "REPORT: " + e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (loadResults != null) {
|
||||||
|
loadResults.release(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return program;
|
private LoadResults<Program> loadPrograms(File file, 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(file, project, folderPath, this,
|
||||||
|
messageLog, TaskMonitor.DUMMY);
|
||||||
|
}
|
||||||
|
return AutoImporter.importByLookingForLcs(file, project, folderPath, options.language,
|
||||||
|
options.compilerSpec, this, messageLog, TaskMonitor.DUMMY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// User specified a loader
|
||||||
|
if (options.language == null) {
|
||||||
|
return AutoImporter.importByUsingSpecificLoaderClass(file, project, folderPath,
|
||||||
|
options.loaderClass, options.loaderArgs, this, messageLog, TaskMonitor.DUMMY);
|
||||||
|
}
|
||||||
|
return AutoImporter.importByUsingSpecificLoaderClassAndLcs(file, project, folderPath,
|
||||||
|
options.loaderClass, options.loaderArgs, options.language, options.compilerSpec, this,
|
||||||
|
messageLog, TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processWithImport(File file, String folderPath, boolean isFirstTime)
|
private void processWithImport(File file, String folderPath, boolean isFirstTime)
|
||||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.app.util.importer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.AccessMode;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
@ -24,10 +25,10 @@ import java.util.function.Predicate;
|
||||||
import generic.stl.Pair;
|
import generic.stl.Pair;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.bin.RandomAccessByteProvider;
|
import ghidra.app.util.bin.FileByteProvider;
|
||||||
import ghidra.app.util.opinion.*;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.framework.model.DomainFolder;
|
import ghidra.formats.gfilesystem.FileSystemService;
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.program.model.address.AddressFactory;
|
import ghidra.program.model.address.AddressFactory;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -38,146 +39,443 @@ import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods to do imports automatically (without requiring user interaction).
|
* Utility methods to do {@link Program} imports automatically (without requiring user interaction)
|
||||||
*/
|
*/
|
||||||
public final class AutoImporter {
|
public final class AutoImporter {
|
||||||
private AutoImporter() {
|
private AutoImporter() {
|
||||||
// service class; cannot instantiate
|
// service class; cannot instantiate
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Program importByUsingBestGuess(File file, DomainFolder programFolder,
|
/**
|
||||||
Object consumer, MessageLog messageLog, TaskMonitor monitor) throws IOException,
|
* Automatically imports the given {@link File} with the best matching {@link Loader} for the
|
||||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
* {@link File}'s format.
|
||||||
List<Program> programs = importFresh(file, programFolder, consumer, messageLog, monitor,
|
* <p>
|
||||||
LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED, null,
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
OptionChooser.DEFAULT_OPTIONS, MultipleProgramsStrategy.ONE_PROGRAM_OR_NULL);
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
if (programs != null && programs.size() == 1) {
|
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
||||||
return programs.get(0);
|
* <p>
|
||||||
}
|
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
||||||
return null;
|
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
||||||
|
*
|
||||||
|
* @param file The {@link File} to import
|
||||||
|
* @param project 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. Could be null if there is no project.
|
||||||
|
* @param projectFolderPath A 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()}.
|
||||||
|
* @param consumer A consumer
|
||||||
|
* @param messageLog The log
|
||||||
|
* @param monitor A task monitor
|
||||||
|
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
||||||
|
* (created but not saved)
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws DuplicateNameException if the load resulted in a {@link Program} naming conflict
|
||||||
|
* @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
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if nothing was loaded
|
||||||
|
*/
|
||||||
|
public static LoadResults<Program> importByUsingBestGuess(File file, Project project,
|
||||||
|
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException, DuplicateNameException, InvalidNameException,
|
||||||
|
VersionException, LoadException {
|
||||||
|
return importFresh(file, project, projectFolderPath, consumer, messageLog,
|
||||||
|
monitor, LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED, null,
|
||||||
|
OptionChooser.DEFAULT_OPTIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Program importByUsingBestGuess(ByteProvider provider, DomainFolder programFolder,
|
/**
|
||||||
Object consumer, MessageLog messageLog, TaskMonitor monitor) throws IOException,
|
* Automatically imports the give {@link ByteProvider bytes} with the best matching
|
||||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
* {@link Loader} for the {@link ByteProvider}'s format.
|
||||||
List<Program> programs = importFresh(provider, programFolder, consumer, messageLog, monitor,
|
* <p>
|
||||||
LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED, null,
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
OptionChooser.DEFAULT_OPTIONS, MultipleProgramsStrategy.ONE_PROGRAM_OR_NULL);
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
if (programs != null && programs.size() == 1) {
|
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
||||||
return programs.get(0);
|
* <p>
|
||||||
}
|
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
||||||
return null;
|
* {@link Program}s with {@link LoadResults#release(Object)} when they are no longer needed.
|
||||||
}
|
*
|
||||||
|
* @param provider The bytes to import
|
||||||
public static Program importByUsingSpecificLoaderClass(File file, DomainFolder programFolder,
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
Class<? extends Loader> loaderClass, List<Pair<String, String>> loaderArgs,
|
* {@link DomainFolder}s and {@link DomainFile}s to do custom behaviors such as loading
|
||||||
Object consumer, MessageLog messageLog, TaskMonitor monitor) throws IOException,
|
* libraries. Could be null if there is no project.
|
||||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
* @param projectFolderPath A suggested project folder path for the {@link Loaded}
|
||||||
SingleLoaderFilter loaderFilter = new SingleLoaderFilter(loaderClass, loaderArgs);
|
* {@link Program}s. This is just a suggestion, and a {@link Loader} implementation
|
||||||
List<Program> programs = importFresh(file, programFolder, consumer, messageLog, monitor,
|
* reserves the right to change it for each {@link Loaded} result. The {@link Loaded} results
|
||||||
loaderFilter, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED, null,
|
* should be queried for their true project folder paths using
|
||||||
new LoaderArgsOptionChooser(loaderFilter),
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
MultipleProgramsStrategy.ONE_PROGRAM_OR_NULL);
|
* @param consumer A consumer
|
||||||
if (programs != null && programs.size() == 1) {
|
* @param messageLog The log
|
||||||
return programs.get(0);
|
* @param monitor A task monitor
|
||||||
}
|
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
||||||
return null;
|
* (created but not saved)
|
||||||
}
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
public static Program importByLookingForLcs(File file, DomainFolder programFolder,
|
* @throws DuplicateNameException if the load resulted in a {@link Program} naming conflict
|
||||||
Language language, CompilerSpec compilerSpec, Object consumer, MessageLog messageLog,
|
* @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
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if nothing was loaded
|
||||||
|
*/
|
||||||
|
public static LoadResults<Program> importByUsingBestGuess(ByteProvider provider,
|
||||||
|
Project project, String projectFolderPath, Object consumer, MessageLog messageLog,
|
||||||
TaskMonitor monitor) throws IOException, CancelledException, DuplicateNameException,
|
TaskMonitor monitor) throws IOException, CancelledException, DuplicateNameException,
|
||||||
InvalidNameException, VersionException {
|
InvalidNameException, VersionException, LoadException {
|
||||||
List<Program> programs = importFresh(file, programFolder, consumer, messageLog, monitor,
|
return importFresh(provider, project, projectFolderPath, consumer, messageLog, monitor,
|
||||||
LoaderService.ACCEPT_ALL, new LcsHintLoadSpecChooser(language, compilerSpec), null,
|
LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED, null,
|
||||||
OptionChooser.DEFAULT_OPTIONS, MultipleProgramsStrategy.ONE_PROGRAM_OR_NULL);
|
OptionChooser.DEFAULT_OPTIONS);
|
||||||
if (programs != null && programs.size() == 1) {
|
|
||||||
return programs.get(0);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Program importByUsingSpecificLoaderClassAndLcs(File file,
|
/**
|
||||||
DomainFolder programFolder, Class<? extends Loader> loaderClass,
|
* Automatically imports the given {@link File} with the given type of {@link Loader}.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param file The {@link File} to import
|
||||||
|
* @param project 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. Could be null if there is no project.
|
||||||
|
* @param projectFolderPath A 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()}.
|
||||||
|
* @param loaderClass The {@link Loader} class to use
|
||||||
|
* @param loaderArgs A {@link List} of optional {@link Loader}-specific arguments
|
||||||
|
* @param consumer A consumer
|
||||||
|
* @param messageLog The log
|
||||||
|
* @param monitor A task monitor
|
||||||
|
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
||||||
|
* (created but not saved)
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws DuplicateNameException if the load resulted in a {@link Program} naming conflict
|
||||||
|
* @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
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if nothing was loaded
|
||||||
|
*/
|
||||||
|
public static LoadResults<Program> importByUsingSpecificLoaderClass(File file,
|
||||||
|
Project project, String projectFolderPath, Class<? extends Loader> loaderClass,
|
||||||
|
List<Pair<String, String>> loaderArgs, Object consumer, MessageLog messageLog,
|
||||||
|
TaskMonitor monitor) throws IOException, CancelledException, DuplicateNameException,
|
||||||
|
InvalidNameException, VersionException, LoadException {
|
||||||
|
SingleLoaderFilter loaderFilter = new SingleLoaderFilter(loaderClass, loaderArgs);
|
||||||
|
return importFresh(file, project, projectFolderPath, consumer, messageLog,
|
||||||
|
monitor, loaderFilter, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED, null,
|
||||||
|
new LoaderArgsOptionChooser(loaderFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically imports the given {@link File} with the best matching {@link Loader} that
|
||||||
|
* supports the given language and compiler specification.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param file The {@link File} to import
|
||||||
|
* @param project 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. Could be null if there is no project.
|
||||||
|
* @param projectFolderPath A 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()}.
|
||||||
|
* @param language The desired {@link Language}
|
||||||
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
|
* @param consumer A consumer
|
||||||
|
* @param messageLog The log
|
||||||
|
* @param monitor A task monitor
|
||||||
|
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
||||||
|
* (created but not saved)
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws DuplicateNameException if the load resulted in a {@link Program} naming conflict
|
||||||
|
* @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
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if nothing was loaded
|
||||||
|
*/
|
||||||
|
public static LoadResults<Program> importByLookingForLcs(File file, Project project,
|
||||||
|
String projectFolderPath, Language language, CompilerSpec compilerSpec, Object consumer,
|
||||||
|
MessageLog messageLog, TaskMonitor monitor) throws IOException, CancelledException,
|
||||||
|
DuplicateNameException, InvalidNameException, VersionException, LoadException {
|
||||||
|
return importFresh(file, project, projectFolderPath, consumer, messageLog,
|
||||||
|
monitor, LoaderService.ACCEPT_ALL, new LcsHintLoadSpecChooser(language, compilerSpec),
|
||||||
|
null, OptionChooser.DEFAULT_OPTIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically imports the given {@link File} with the given type of {@link Loader}, language,
|
||||||
|
* and compiler specification.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param file The {@link File} to import
|
||||||
|
* @param project 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. Could be null if there is no project.
|
||||||
|
* @param projectFolderPath A 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()}.
|
||||||
|
* @param loaderClass The {@link Loader} class to use
|
||||||
|
* @param loaderArgs A {@link List} of optional {@link Loader}-specific arguments
|
||||||
|
* @param language The desired {@link Language}
|
||||||
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
|
* @param consumer A consumer
|
||||||
|
* @param messageLog The log
|
||||||
|
* @param monitor A task monitor
|
||||||
|
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
||||||
|
* (created but not saved)
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws DuplicateNameException if the load resulted in a {@link Program} naming conflict
|
||||||
|
* @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
|
||||||
|
* failed language upgrade
|
||||||
|
*/
|
||||||
|
public static LoadResults<Program> importByUsingSpecificLoaderClassAndLcs(File file,
|
||||||
|
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);
|
SingleLoaderFilter loaderFilter = new SingleLoaderFilter(loaderClass, loaderArgs);
|
||||||
List<Program> programs = importFresh(file, programFolder, consumer, messageLog, monitor,
|
return importFresh(file, project, projectFolderPath, consumer, messageLog,
|
||||||
loaderFilter, new LcsHintLoadSpecChooser(language, compilerSpec), null,
|
monitor, loaderFilter, new LcsHintLoadSpecChooser(language, compilerSpec), null,
|
||||||
new LoaderArgsOptionChooser(loaderFilter),
|
new LoaderArgsOptionChooser(loaderFilter));
|
||||||
MultipleProgramsStrategy.ONE_PROGRAM_OR_NULL);
|
|
||||||
if (programs != null && programs.size() == 1) {
|
|
||||||
return programs.get(0);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Predicate<Loader> BINARY_LOADER =
|
private static final Predicate<Loader> BINARY_LOADER =
|
||||||
new SingleLoaderFilter(BinaryLoader.class);
|
new SingleLoaderFilter(BinaryLoader.class);
|
||||||
|
|
||||||
public static Program importAsBinary(File file, DomainFolder programFolder, Language language,
|
/**
|
||||||
CompilerSpec compilerSpec, Object consumer, MessageLog messageLog, TaskMonitor monitor)
|
* Automatically imports the given {@link File} with the {@link BinaryLoader}, using the given
|
||||||
throws IOException, CancelledException, DuplicateNameException, InvalidNameException,
|
* language and compiler specification.
|
||||||
VersionException {
|
* <p>
|
||||||
List<Program> programs = importFresh(file, programFolder, consumer, messageLog, monitor,
|
* Note that when the import completes, the returned {@link Loaded} {@link Program} is
|
||||||
BINARY_LOADER, new LcsHintLoadSpecChooser(language, compilerSpec), null,
|
* not saved to a project. That is the responsibility of the caller (see
|
||||||
OptionChooser.DEFAULT_OPTIONS, MultipleProgramsStrategy.ONE_PROGRAM_OR_NULL);
|
* {@link Loaded#save(Project, MessageLog, TaskMonitor)}).
|
||||||
if (programs != null && programs.size() == 1) {
|
* <p>
|
||||||
return programs.get(0);
|
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
||||||
}
|
* {@link Program} with {@link Loaded#release(Object)} when it is no longer needed.
|
||||||
return null;
|
*
|
||||||
|
* @param file The {@link File} to import
|
||||||
|
* @param project 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. Could be null if there is no project.
|
||||||
|
* @param projectFolderPath A suggested project folder path for the {@link Loaded}
|
||||||
|
* {@link Program}. This is just a suggestion, and a {@link Loader} implementation
|
||||||
|
* reserves the right to change it for the {@link Loaded} result. The {@link Loaded} result
|
||||||
|
* should be queried for its true project folder paths using
|
||||||
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
|
* @param language The desired {@link Language}
|
||||||
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
|
* @param consumer A consumer
|
||||||
|
* @param messageLog The log
|
||||||
|
* @param monitor A task monitor
|
||||||
|
* @return The {@link Loaded} {@link Program} (created but not saved)
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws DuplicateNameException if the load resulted in a {@link Program} naming conflict
|
||||||
|
* @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
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if nothing was loaded
|
||||||
|
*/
|
||||||
|
public static Loaded<Program> importAsBinary(File file, Project project,
|
||||||
|
String projectFolderPath, Language language, CompilerSpec compilerSpec, Object consumer,
|
||||||
|
MessageLog messageLog, TaskMonitor monitor) throws IOException, CancelledException,
|
||||||
|
DuplicateNameException, InvalidNameException, VersionException, LoadException {
|
||||||
|
LoadResults<Program> loadResults = importFresh(file, project, projectFolderPath, consumer,
|
||||||
|
messageLog, monitor, BINARY_LOADER, new LcsHintLoadSpecChooser(language, compilerSpec),
|
||||||
|
null, OptionChooser.DEFAULT_OPTIONS);
|
||||||
|
loadResults.releaseNonPrimary(consumer);
|
||||||
|
return loadResults.getPrimary();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Program importAsBinary(ByteProvider bytes, DomainFolder programFolder,
|
/**
|
||||||
Language language, CompilerSpec compilerSpec, Object consumer, MessageLog messageLog,
|
* Automatically imports the given {@link ByteProvider} bytes with the {@link BinaryLoader},
|
||||||
TaskMonitor monitor) throws IOException, CancelledException, DuplicateNameException,
|
* using the given language and compiler specification.
|
||||||
InvalidNameException, VersionException {
|
* <p>
|
||||||
List<Program> programs = importFresh(bytes, programFolder, consumer, messageLog, monitor,
|
* Note that when the import completes, the returned {@link Loaded} {@link Program} is
|
||||||
BINARY_LOADER, new LcsHintLoadSpecChooser(language, compilerSpec), null,
|
* not saved to a project. That is the responsibility of the caller (see
|
||||||
OptionChooser.DEFAULT_OPTIONS, MultipleProgramsStrategy.ONE_PROGRAM_OR_NULL);
|
* {@link Loaded#save(Project, MessageLog, TaskMonitor)}).
|
||||||
if (programs != null && programs.size() == 1) {
|
* <p>
|
||||||
return programs.get(0);
|
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
||||||
}
|
* {@link Program} with {@link Loaded#release(Object)} when it is no longer needed.
|
||||||
return null;
|
*
|
||||||
|
* @param bytes The bytes to import
|
||||||
|
* @param project 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. Could be null if there is no project.
|
||||||
|
* @param projectFolderPath A suggested project folder path for the {@link Loaded}
|
||||||
|
* {@link Program}. This is just a suggestion, and a {@link Loader} implementation
|
||||||
|
* reserves the right to change it the {@link Loaded} result. The {@link Loaded} result
|
||||||
|
* should be queried for its true project folder paths using
|
||||||
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
|
* @param language The desired {@link Language}
|
||||||
|
* @param compilerSpec The desired {@link CompilerSpec compiler specification}
|
||||||
|
* @param consumer A consumer
|
||||||
|
* @param messageLog The log
|
||||||
|
* @param monitor A task monitor
|
||||||
|
* @return The {@link Loaded} {@link Program} (created but not saved)
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws DuplicateNameException if the load resulted in a {@link Program} naming conflict
|
||||||
|
* @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
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if nothing was loaded
|
||||||
|
*/
|
||||||
|
public static Loaded<Program> importAsBinary(ByteProvider bytes, Project project,
|
||||||
|
String projectFolderPath, Language language, CompilerSpec compilerSpec,
|
||||||
|
Object consumer, MessageLog messageLog, TaskMonitor monitor) throws IOException,
|
||||||
|
CancelledException, DuplicateNameException, InvalidNameException, VersionException,
|
||||||
|
LoadException {
|
||||||
|
LoadResults<Program> loadResults = importFresh(bytes, project, projectFolderPath, consumer,
|
||||||
|
messageLog, monitor, BINARY_LOADER, new LcsHintLoadSpecChooser(language, compilerSpec),
|
||||||
|
null, OptionChooser.DEFAULT_OPTIONS);
|
||||||
|
loadResults.releaseNonPrimary(consumer);
|
||||||
|
return loadResults.getPrimary();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Program> importFresh(File file, DomainFolder programFolder, Object consumer,
|
/**
|
||||||
MessageLog messageLog, TaskMonitor monitor, Predicate<Loader> loaderFilter,
|
* Automatically imports the given {@link File} with advanced options.
|
||||||
LoadSpecChooser loadSpecChooser, String programNameOverride,
|
* <p>
|
||||||
OptionChooser optionChooser, MultipleProgramsStrategy multipleProgramsStrategy)
|
* Note that when the import completes, the returned {@link Loaded} {@link Program}s are not
|
||||||
throws IOException, CancelledException, DuplicateNameException, InvalidNameException,
|
* saved to a project. That is the responsibility of the caller (see
|
||||||
VersionException {
|
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
||||||
if (file == null) {
|
* <p>
|
||||||
return null;
|
* 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.
|
||||||
|
*
|
||||||
try (ByteProvider provider = new RandomAccessByteProvider(file)) {
|
* @param file The {@link File} to import
|
||||||
return importFresh(provider, programFolder, consumer, messageLog, monitor, loaderFilter,
|
* @param project The {@link Project}. Loaders can use this to take advantage of existing
|
||||||
loadSpecChooser, programNameOverride, optionChooser, multipleProgramsStrategy);
|
* {@link DomainFolder}s and {@link DomainFile}s to do custom behaviors such as loading
|
||||||
}
|
* libraries. Could be null if there is no project.
|
||||||
}
|
* @param projectFolderPath A suggested project folder path for the {@link Loaded}
|
||||||
|
* {@link Program}s. This is just a suggestion, and a {@link Loader} implementation
|
||||||
public static List<Program> importFresh(ByteProvider provider, DomainFolder programFolder,
|
* reserves the right to change it for each {@link Loaded} result. The {@link Loaded} results
|
||||||
Object consumer, MessageLog messageLog, TaskMonitor monitor,
|
* should be queried for their true project folder paths using
|
||||||
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
|
* @param loaderFilter A {@link Predicate} used to choose what {@link Loader}(s) get used
|
||||||
|
* @param loadSpecChooser A {@link LoadSpecChooser} used to choose what {@link LoadSpec}(s) get
|
||||||
|
* used
|
||||||
|
* @param importNameOverride The name to use for the imported thing. Null to use the
|
||||||
|
* {@link Loader}'s preferred name.
|
||||||
|
* @param optionChooser A {@link OptionChooser} used to choose what {@link Loader} options get
|
||||||
|
* used
|
||||||
|
* @param consumer A consumer
|
||||||
|
* @param messageLog The log
|
||||||
|
* @param monitor A task monitor
|
||||||
|
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
||||||
|
* (created but not saved)
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws DuplicateNameException if the load resulted in a {@link Program} naming conflict
|
||||||
|
* @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
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if nothing was loaded
|
||||||
|
*/
|
||||||
|
public static LoadResults<Program> importFresh(File file, Project project,
|
||||||
|
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor,
|
||||||
Predicate<Loader> loaderFilter, LoadSpecChooser loadSpecChooser,
|
Predicate<Loader> loaderFilter, LoadSpecChooser loadSpecChooser,
|
||||||
String programNameOverride, OptionChooser optionChooser,
|
String importNameOverride, OptionChooser optionChooser) throws IOException,
|
||||||
MultipleProgramsStrategy multipleProgramsStrategy) throws IOException,
|
CancelledException, DuplicateNameException, InvalidNameException, VersionException,
|
||||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
LoadException {
|
||||||
|
if (file == null) {
|
||||||
|
throw new LoadException("Cannot load null file");
|
||||||
|
}
|
||||||
|
|
||||||
|
try (ByteProvider provider = new FileByteProvider(file,
|
||||||
|
FileSystemService.getInstance().getLocalFSRL(file), AccessMode.READ)) {
|
||||||
|
return importFresh(provider, project, projectFolderPath, consumer, messageLog, monitor,
|
||||||
|
loaderFilter, loadSpecChooser, importNameOverride, optionChooser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically imports the given {@link ByteProvider bytes} with advanced options.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param provider The bytes to import
|
||||||
|
* @param project 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. Could be null if there is no project.
|
||||||
|
* @param projectFolderPath A 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()}.
|
||||||
|
* @param loaderFilter A {@link Predicate} used to choose what {@link Loader}(s) get used
|
||||||
|
* @param loadSpecChooser A {@link LoadSpecChooser} used to choose what {@link LoadSpec}(s) get
|
||||||
|
* used
|
||||||
|
* @param importNameOverride The name to use for the imported thing. Null to use the
|
||||||
|
* {@link Loader}'s preferred name.
|
||||||
|
* @param optionChooser A {@link OptionChooser} used to choose what {@link Loader} options get
|
||||||
|
* used
|
||||||
|
* @param consumer A consumer
|
||||||
|
* @param messageLog The log
|
||||||
|
* @param monitor A task monitor
|
||||||
|
* @return The {@link LoadResults} which contains one ore more {@link Loaded} {@link Program}s
|
||||||
|
* (created but not saved)
|
||||||
|
* @throws IOException if there was an IO-related problem loading
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws DuplicateNameException if the load resulted in a {@link Program} naming conflict
|
||||||
|
* @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
|
||||||
|
* failed language upgrade
|
||||||
|
* @throws LoadException if nothing was loaded
|
||||||
|
*/
|
||||||
|
public static LoadResults<Program> importFresh(ByteProvider provider, Project project,
|
||||||
|
String projectFolderPath, Object consumer, MessageLog messageLog, TaskMonitor monitor,
|
||||||
|
Predicate<Loader> loaderFilter, LoadSpecChooser loadSpecChooser,
|
||||||
|
String importNameOverride, OptionChooser optionChooser) throws IOException,
|
||||||
|
CancelledException, DuplicateNameException, InvalidNameException, VersionException,
|
||||||
|
LoadException {
|
||||||
|
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
return null;
|
throw new LoadException("Cannot load null provider");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the load spec
|
// Get the load spec
|
||||||
LoadSpec loadSpec = getLoadSpec(loaderFilter, loadSpecChooser, provider);
|
LoadSpec loadSpec = getLoadSpec(loaderFilter, loadSpecChooser, provider);
|
||||||
if (loadSpec == null) {
|
if (loadSpec == null) {
|
||||||
return null;
|
throw new LoadException("No load spec found");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the program name
|
// Get the preferred import name
|
||||||
String programName = loadSpec.getLoader().getPreferredFileName(provider);
|
String importName = loadSpec.getLoader().getPreferredFileName(provider);
|
||||||
if (programNameOverride != null) {
|
if (importNameOverride != null) {
|
||||||
programName = programNameOverride;
|
importName = importNameOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect options
|
// Collect options
|
||||||
|
@ -185,21 +483,40 @@ public final class AutoImporter {
|
||||||
AddressFactory addrFactory = null;// Address type options not permitted if null
|
AddressFactory addrFactory = null;// Address type options not permitted if null
|
||||||
if (languageCompilerSpecPair != null) {
|
if (languageCompilerSpecPair != null) {
|
||||||
// It is assumed that if languageCompilerSpecPair exists, then language will be found
|
// It is assumed that if languageCompilerSpecPair exists, then language will be found
|
||||||
addrFactory = DefaultLanguageService.getLanguageService().getLanguage(
|
addrFactory = DefaultLanguageService.getLanguageService()
|
||||||
languageCompilerSpecPair.languageID).getAddressFactory();
|
.getLanguage(
|
||||||
|
languageCompilerSpecPair.languageID)
|
||||||
|
.getAddressFactory();
|
||||||
}
|
}
|
||||||
List<Option> loaderOptions = optionChooser.choose(
|
List<Option> loaderOptions = optionChooser.choose(
|
||||||
loadSpec.getLoader().getDefaultOptions(provider, loadSpec, null, false), addrFactory);
|
loadSpec.getLoader().getDefaultOptions(provider, loadSpec, null, false), addrFactory);
|
||||||
if (loaderOptions == null) {
|
if (loaderOptions == null) {
|
||||||
return null;
|
throw new LoadException("Cannot load with null options");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import program
|
// Import
|
||||||
Msg.info(AutoImporter.class, "Using Loader: " + loadSpec.getLoader().getName());
|
Msg.info(AutoImporter.class, "Using Loader: " + loadSpec.getLoader().getName());
|
||||||
List<DomainObject> domainObjects = loadSpec.getLoader().load(provider, programName,
|
Msg.info(AutoImporter.class,
|
||||||
programFolder, loadSpec, loaderOptions, messageLog, consumer, monitor);
|
"Using Language/Compiler: " + loadSpec.getLanguageCompilerSpec());
|
||||||
|
LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||||
|
.load(provider, importName, project, projectFolderPath, loadSpec, loaderOptions,
|
||||||
|
messageLog, consumer, monitor);
|
||||||
|
|
||||||
return multipleProgramsStrategy.handlePrograms(getPrograms(domainObjects), consumer);
|
// 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,
|
private static LoadSpec getLoadSpec(Predicate<Loader> loaderFilter,
|
||||||
|
@ -216,15 +533,4 @@ public final class AutoImporter {
|
||||||
Msg.info(AutoImporter.class, "No load spec found for import file: " + name);
|
Msg.info(AutoImporter.class, "No load spec found for import file: " + name);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Program> getPrograms(List<DomainObject> domainObjects) {
|
|
||||||
List<Program> programs = new ArrayList<Program>();
|
|
||||||
for (DomainObject domainObject : domainObjects) {
|
|
||||||
if (domainObject instanceof Program) {
|
|
||||||
programs.add((Program) domainObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return programs;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
|
||||||
* 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 ghidra.program.model.listing.Program;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface MultipleProgramsStrategy {
|
|
||||||
public static final MultipleProgramsStrategy ALL_PROGRAMS = new MultipleProgramsStrategy() {
|
|
||||||
public List<Program> handlePrograms(List<Program> programs,
|
|
||||||
Object consumer) {
|
|
||||||
return programs;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MultipleProgramsStrategy ONE_PROGRAM_OR_EXCEPTION = new MultipleProgramsStrategy() {
|
|
||||||
public List<Program> handlePrograms(List<Program> programs,
|
|
||||||
Object consumer) {
|
|
||||||
if (programs != null && programs.size() > 1) {
|
|
||||||
for (Program program : programs) {
|
|
||||||
program.release(consumer);
|
|
||||||
}
|
|
||||||
throw new MultipleProgramsException();
|
|
||||||
}
|
|
||||||
return programs;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final MultipleProgramsStrategy ONE_PROGRAM_OR_NULL = new MultipleProgramsStrategy() {
|
|
||||||
public List<Program> handlePrograms(List<Program> programs,
|
|
||||||
Object consumer) {
|
|
||||||
if (programs != null && programs.size() > 1) {
|
|
||||||
for (Program program : programs) {
|
|
||||||
program.release(consumer);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return programs;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
List<Program> handlePrograms(List<Program> programs, Object consumer);
|
|
||||||
}
|
|
|
@ -24,6 +24,7 @@ import java.util.stream.Collectors;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
|
import ghidra.app.util.OptionUtils;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.bin.FileByteProvider;
|
import ghidra.app.util.bin.FileByteProvider;
|
||||||
import ghidra.app.util.importer.LibrarySearchPathManager;
|
import ghidra.app.util.importer.LibrarySearchPathManager;
|
||||||
|
@ -85,23 +86,24 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
throws CancelledException, IOException;
|
throws CancelledException, IOException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String loadedName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
Object consumer, TaskMonitor monitor) throws CancelledException, IOException {
|
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||||
|
throws CancelledException, IOException {
|
||||||
|
|
||||||
List<LoadedProgram> loadedProgramList = new ArrayList<>();
|
List<Loaded<Program>> loadedProgramList = new ArrayList<>();
|
||||||
List<String> libraryNameList = new ArrayList<>();
|
List<String> libraryNameList = new ArrayList<>();
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
// Load the primary program
|
// Load the primary program
|
||||||
Program program = doLoad(provider, programName, programFolder, loadSpec,
|
Program program = doLoad(provider, loadedName, loadSpec, libraryNameList, options,
|
||||||
libraryNameList, options, consumer, log, monitor);
|
consumer, log, monitor);
|
||||||
loadedProgramList.add(new LoadedProgram(program, programFolder));
|
loadedProgramList.add(new Loaded<>(program, loadedName, projectFolderPath));
|
||||||
|
|
||||||
// Load the libraries
|
// Load the libraries
|
||||||
List<LoadedProgram> libraries = loadLibraries(provider, program, programFolder,
|
List<Loaded<Program>> libraries = loadLibraries(provider, program, project,
|
||||||
loadSpec, options, log, consumer, libraryNameList, monitor);
|
projectFolderPath, loadSpec, options, log, consumer, libraryNameList, monitor);
|
||||||
loadedProgramList.addAll(libraries);
|
loadedProgramList.addAll(libraries);
|
||||||
|
|
||||||
success = true;
|
success = true;
|
||||||
|
@ -115,35 +117,46 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||||
List<Option> options, MessageLog log, Program program, TaskMonitor monitor)
|
List<Option> options, MessageLog log, Program program, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, LoadException, IOException {
|
||||||
|
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
LanguageID languageID = program.getLanguageID();
|
LanguageID languageID = program.getLanguageID();
|
||||||
CompilerSpecID compilerSpecID = program.getCompilerSpec().getCompilerSpecID();
|
CompilerSpecID compilerSpecID = program.getCompilerSpec().getCompilerSpecID();
|
||||||
if (!(pair.languageID.equals(languageID) && pair.compilerSpecID.equals(compilerSpecID))) {
|
if (!(pair.languageID.equals(languageID) && pair.compilerSpecID.equals(compilerSpecID))) {
|
||||||
log.appendMsg(provider.getAbsolutePath() +
|
String message = provider.getAbsolutePath() +
|
||||||
" does not have the same language/compiler spec as program " + program.getName());
|
" does not have the same language/compiler spec as program " + program.getName();
|
||||||
return false;
|
log.appendMsg(message);
|
||||||
|
throw new LoadException(message);
|
||||||
}
|
}
|
||||||
log.appendMsg("----- Loading " + provider.getAbsolutePath() + " -----");
|
log.appendMsg("----- Loading " + provider.getAbsolutePath() + " -----");
|
||||||
load(provider, loadSpec, options, program, monitor, log);
|
load(provider, loadSpec, options, program, monitor, log);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
|
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||||
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
|
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||||
|
throws CancelledException, IOException {
|
||||||
if (loadedPrograms.isEmpty()) {
|
if (loadedPrograms.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isLinkExistingLibraries(options) || isLoadLocalLibraries(options) ||
|
if (isLinkExistingLibraries(options) || isLoadLocalLibraries(options) ||
|
||||||
isLoadSystemLibraries(options)) {
|
isLoadSystemLibraries(options)) {
|
||||||
DomainFolder programFolder = loadedPrograms.get(0).destinationFolder();
|
String projectFolderPath = loadedPrograms.get(0).getProjectFolderPath();
|
||||||
DomainFolder linkSearchFolder = getLinkSearchFolder(programFolder, options);
|
List<DomainFolder> searchFolders = new ArrayList<>();
|
||||||
fixupExternalLibraries(loadedPrograms.stream().map(e -> e.program()).toList(),
|
String destPath = getLibraryDestinationFolderPath(project, projectFolderPath, options);
|
||||||
linkSearchFolder, true, messageLog, monitor);
|
DomainFolder destSearchFolder =
|
||||||
|
getLibraryDestinationSearchFolder(project, destPath, options);
|
||||||
|
DomainFolder linkSearchFolder =
|
||||||
|
getLinkSearchFolder(project, projectFolderPath, options);
|
||||||
|
if (destSearchFolder != null) {
|
||||||
|
searchFolders.add(destSearchFolder);
|
||||||
|
}
|
||||||
|
if (linkSearchFolder != null) {
|
||||||
|
searchFolders.add(linkSearchFolder);
|
||||||
|
}
|
||||||
|
fixupExternalLibraries(loadedPrograms, searchFolders, messageLog, monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +213,11 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
if (!String.class.isAssignableFrom(option.getValueClass())) {
|
if (!String.class.isAssignableFrom(option.getValueClass())) {
|
||||||
return "Invalid type for option: " + name + " - " + option.getValueClass();
|
return "Invalid type for option: " + name + " - " + option.getValueClass();
|
||||||
}
|
}
|
||||||
|
String value = (String) option.getValue();
|
||||||
|
if (!value.isEmpty() && !value.startsWith("/")) {
|
||||||
|
return "Invalid absolute project path for option: " + name;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,46 +231,41 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @return True if existing libraries should be linked; otherwise, false
|
* @return True if existing libraries should be linked; otherwise, false
|
||||||
*/
|
*/
|
||||||
protected boolean isLinkExistingLibraries(List<Option> options) {
|
protected boolean isLinkExistingLibraries(List<Option> options) {
|
||||||
boolean isLinkExistingLibraries = LINK_EXISTING_OPTION_DEFAULT;
|
return OptionUtils.getOption(LINK_EXISTING_OPTION_NAME, options,
|
||||||
if (options != null) {
|
LINK_EXISTING_OPTION_DEFAULT);
|
||||||
for (Option option : options) {
|
|
||||||
String optName = option.getName();
|
|
||||||
if (optName.equals(LINK_EXISTING_OPTION_NAME)) {
|
|
||||||
isLinkExistingLibraries = (Boolean) option.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isLinkExistingLibraries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link DomainFolder project folder} to search for existing libraries
|
* Gets the {@link DomainFolder project folder} to search for existing libraries
|
||||||
*
|
*
|
||||||
* @param programFolder The {@link DomainFolder} that the main program is being loaded into
|
* @param project The {@link Project}. Could be null if there is no project.
|
||||||
|
* @param projectFolderPath The project folder path the program will get saved to. Could be null
|
||||||
|
* if the program is not getting saved to the project.
|
||||||
* @param options a {@link List} of {@link Option}s
|
* @param options a {@link List} of {@link Option}s
|
||||||
* @return The path of the project folder to search for existing libraries, or null if no
|
* @return The path of the project folder to search for existing libraries, or null if no
|
||||||
* project folders should be searched
|
* project folders can be or should be searched
|
||||||
*/
|
*/
|
||||||
protected DomainFolder getLinkSearchFolder(DomainFolder programFolder,
|
protected DomainFolder getLinkSearchFolder(Project project, String projectFolderPath,
|
||||||
List<Option> options) {
|
List<Option> options) {
|
||||||
if (!shouldSearchAllPaths(options) && !isLinkExistingLibraries(options)) {
|
if (!shouldSearchAllPaths(options) && !isLinkExistingLibraries(options)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String folderPath = LINK_SEARCH_FOLDER_OPTION_DEFAULT;
|
if (project == null) {
|
||||||
if (options != null) {
|
return null;
|
||||||
for (Option option : options) {
|
|
||||||
String optName = option.getName();
|
|
||||||
if (optName.equals(LINK_SEARCH_FOLDER_OPTION_NAME)) {
|
|
||||||
folderPath = (String) option.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (folderPath.equals(LINK_SEARCH_FOLDER_OPTION_DEFAULT)) {
|
String linkSearchFolderPath = OptionUtils.getOption(LINK_SEARCH_FOLDER_OPTION_NAME, options,
|
||||||
return programFolder;
|
LINK_SEARCH_FOLDER_OPTION_DEFAULT);
|
||||||
|
|
||||||
|
ProjectData projectData = project.getProjectData();
|
||||||
|
if (linkSearchFolderPath.isBlank()) {
|
||||||
|
if (projectFolderPath == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return projectData.getFolder(projectFolderPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return programFolder.getProjectData().getFolder(FilenameUtils.separatorsToUnix(folderPath));
|
return projectData.getFolder(FilenameUtils.separatorsToUnix(linkSearchFolderPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -263,16 +276,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @return True if local libraries should be loaded; otherwise, false
|
* @return True if local libraries should be loaded; otherwise, false
|
||||||
*/
|
*/
|
||||||
protected boolean isLoadLocalLibraries(List<Option> options) {
|
protected boolean isLoadLocalLibraries(List<Option> options) {
|
||||||
boolean isLoadLocalLibraries = LOCAL_LIBRARY_OPTION_DEFAULT;
|
return OptionUtils.getOption(LOCAL_LIBRARY_OPTION_NAME, options,
|
||||||
if (options != null) {
|
LOCAL_LIBRARY_OPTION_DEFAULT);
|
||||||
for (Option option : options) {
|
|
||||||
String optName = option.getName();
|
|
||||||
if (optName.equals(LOCAL_LIBRARY_OPTION_NAME)) {
|
|
||||||
isLoadLocalLibraries = (Boolean) option.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isLoadLocalLibraries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -283,16 +288,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @return True if system libraries should be loaded; otherwise, false
|
* @return True if system libraries should be loaded; otherwise, false
|
||||||
*/
|
*/
|
||||||
protected boolean isLoadSystemLibraries(List<Option> options) {
|
protected boolean isLoadSystemLibraries(List<Option> options) {
|
||||||
boolean isLoadSystemLibraries = SYSTEM_LIBRARY_OPTION_DEFAULT;
|
return OptionUtils.getOption(SYSTEM_LIBRARY_OPTION_NAME, options,
|
||||||
if (options != null) {
|
SYSTEM_LIBRARY_OPTION_DEFAULT);
|
||||||
for (Option option : options) {
|
|
||||||
String optName = option.getName();
|
|
||||||
if (optName.equals(SYSTEM_LIBRARY_OPTION_NAME)) {
|
|
||||||
isLoadSystemLibraries = (Boolean) option.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isLoadSystemLibraries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -302,42 +299,57 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @return The desired recursive library load depth
|
* @return The desired recursive library load depth
|
||||||
*/
|
*/
|
||||||
protected int getLibraryLoadDepth(List<Option> options) {
|
protected int getLibraryLoadDepth(List<Option> options) {
|
||||||
int depth = DEPTH_OPTION_DEFAULT;
|
return OptionUtils.getOption(DEPTH_OPTION_NAME, options, DEPTH_OPTION_DEFAULT);
|
||||||
if (options != null) {
|
|
||||||
for (Option option : options) {
|
|
||||||
String optName = option.getName();
|
|
||||||
if (optName.equals(DEPTH_OPTION_NAME)) {
|
|
||||||
depth = (int) option.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return depth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link DomainFolder project folder} to load the libraries into
|
* Gets the project folder path to load the libraries into. It does not have to exist in the
|
||||||
|
* project yet.
|
||||||
*
|
*
|
||||||
* @param programFolder The {@link DomainFolder} that the main program is being loaded into
|
* @param project The {@link Project}. Could be null if there is no project.
|
||||||
|
* @param projectFolderPath The project folder path the program will get saved to. Could be null
|
||||||
|
* if the program is not getting saved to the project.
|
||||||
* @param options a {@link List} of {@link Option}s
|
* @param options a {@link List} of {@link Option}s
|
||||||
* @return The path of the project folder to load the libraries into
|
* @return 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 determined.
|
||||||
*/
|
*/
|
||||||
protected DomainFolder getLibraryDestinationFolder(DomainFolder programFolder,
|
protected String getLibraryDestinationFolderPath(Project project, String projectFolderPath,
|
||||||
List<Option> options) {
|
List<Option> options) {
|
||||||
String folderPath = LIBRARY_DEST_FOLDER_OPTION_DEFAULT;
|
if (project == null) {
|
||||||
if (options != null) {
|
return null;
|
||||||
for (Option option : options) {
|
|
||||||
String optName = option.getName();
|
|
||||||
if (optName.equals(LIBRARY_DEST_FOLDER_OPTION_NAME)) {
|
|
||||||
folderPath = (String) option.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (folderPath.equals(LIBRARY_DEST_FOLDER_OPTION_DEFAULT)) {
|
String libraryDestinationFolderPath = OptionUtils.getOption(LIBRARY_DEST_FOLDER_OPTION_NAME,
|
||||||
return programFolder;
|
options, LIBRARY_DEST_FOLDER_OPTION_DEFAULT);
|
||||||
|
|
||||||
|
if (libraryDestinationFolderPath.isBlank()) {
|
||||||
|
return projectFolderPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
return programFolder.getProjectData().getFolder(FilenameUtils.separatorsToUnix(folderPath));
|
return FilenameUtils.separatorsToUnix(libraryDestinationFolderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link DomainFolder project folder} that libraries are loaded into, to search for
|
||||||
|
* existing libraries. It will only be returned if the options to load new libraries into the
|
||||||
|
* project are set.
|
||||||
|
*
|
||||||
|
* @param project The {@link Project}. Could be null if there is no project.
|
||||||
|
* @param libraryDestinationFolderPath The path of the project folder to load the libraries
|
||||||
|
* into. Could be null (@see #getLibraryDestinationFolderPath(Project, String, List)).
|
||||||
|
* @param options a {@link List} of {@link Option}s
|
||||||
|
* @return The path of the destination project folder to search for existing libraries, or null
|
||||||
|
* if the destination folder is not being used or should not be searched
|
||||||
|
*/
|
||||||
|
protected DomainFolder getLibraryDestinationSearchFolder(Project project,
|
||||||
|
String libraryDestinationFolderPath, List<Option> options) {
|
||||||
|
if (project == null || libraryDestinationFolderPath == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!isLoadLocalLibraries(options) && !isLoadSystemLibraries(options)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return project.getProjectData().getFolder(libraryDestinationFolderPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -424,8 +436,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @param loadSpec The {@link LoadSpec} used for the load
|
* @param loadSpec The {@link LoadSpec} used for the load
|
||||||
* @param options The options
|
* @param options The options
|
||||||
* @param log The log
|
* @param log The log
|
||||||
* @param monitor A cancel.able monitor
|
* @param monitor A cancelable monitor
|
||||||
* @return True if the library should be saved to the project; otherwise, false
|
* @return True if the library should be loaded into the project; otherwise, false
|
||||||
* @throws IOException If an IO-related error occurred
|
* @throws IOException If an IO-related error occurred
|
||||||
* @throws CancelledException If the user cancelled the action
|
* @throws CancelledException If the user cancelled the action
|
||||||
*/
|
*/
|
||||||
|
@ -440,9 +452,9 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
*
|
*
|
||||||
* @param provider The {@link ByteProvider} of the program being loaded
|
* @param provider The {@link ByteProvider} of the program being loaded
|
||||||
* @param program The {@link Program} being loaded
|
* @param program The {@link Program} being loaded
|
||||||
* @param programFolder The domain folder where the new program will be stored, if null
|
* @param project The {@link Project}. Could be null if there is no project.
|
||||||
* the program should not be pre-saved. NOTE: the newly imported libraries will not be written
|
* @param projectFolderPath The project folder path the program will get saved to. Could be null
|
||||||
* to this folder yet, that is handled in a later follow on step.
|
* if the program is not getting saved to the project.
|
||||||
* @param desiredLoadSpec The desired {@link LoadSpec}
|
* @param desiredLoadSpec The desired {@link LoadSpec}
|
||||||
* @param options The load options
|
* @param options The load options
|
||||||
* @param log The log
|
* @param log The log
|
||||||
|
@ -453,19 +465,25 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the user cancelled the load
|
* @throws CancelledException if the user cancelled the load
|
||||||
*/
|
*/
|
||||||
private List<LoadedProgram> loadLibraries(ByteProvider provider, Program program,
|
private List<Loaded<Program>> loadLibraries(ByteProvider provider, Program program,
|
||||||
DomainFolder programFolder, LoadSpec desiredLoadSpec, List<Option> options,
|
Project project, String projectFolderPath, LoadSpec desiredLoadSpec,
|
||||||
MessageLog log, Object consumer, List<String> libraryNameList, TaskMonitor monitor)
|
List<Option> options, MessageLog log, Object consumer, List<String> libraryNameList,
|
||||||
throws CancelledException, IOException {
|
TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
|
|
||||||
List<LoadedProgram> loadedPrograms = new ArrayList<>();
|
List<Loaded<Program>> loadedPrograms = new ArrayList<>();
|
||||||
Set<String> processed = new HashSet<>();
|
Program library = null;
|
||||||
|
Set<String> processed = new TreeSet<>(getLibraryNameComparator());
|
||||||
Queue<UnprocessedLibrary> unprocessed =
|
Queue<UnprocessedLibrary> unprocessed =
|
||||||
createUnprocessedQueue(libraryNameList, getLibraryLoadDepth(options));
|
createUnprocessedQueue(libraryNameList, getLibraryLoadDepth(options));
|
||||||
List<String> searchPaths = getLibrarySearchPaths(provider, options);
|
List<String> searchPaths = getLibrarySearchPaths(provider, options);
|
||||||
DomainFolder linkSearchFolder = getLinkSearchFolder(programFolder, options);
|
DomainFolder linkSearchFolder = getLinkSearchFolder(project, projectFolderPath, options);
|
||||||
DomainFolder libraryDestFolder = getLibraryDestinationFolder(programFolder, options);
|
String libraryDestFolderPath =
|
||||||
|
getLibraryDestinationFolderPath(project, projectFolderPath, options);
|
||||||
|
DomainFolder libraryDestFolder =
|
||||||
|
getLibraryDestinationSearchFolder(project, libraryDestFolderPath, options);
|
||||||
|
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
while (!unprocessed.isEmpty()) {
|
while (!unprocessed.isEmpty()) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
UnprocessedLibrary unprocessedLibrary = unprocessed.remove();
|
UnprocessedLibrary unprocessedLibrary = unprocessed.remove();
|
||||||
|
@ -476,7 +494,12 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
processed.add(libraryName);
|
processed.add(libraryName);
|
||||||
boolean foundLibrary = false;
|
boolean foundLibrary = false;
|
||||||
if (linkSearchFolder != null && findLibrary(libraryName, linkSearchFolder) != null) {
|
if (libraryDestFolder != null &&
|
||||||
|
findLibrary(libraryName, libraryDestFolder) != null) {
|
||||||
|
log.appendMsg("Library " + libraryName + ": Already loaded ");
|
||||||
|
}
|
||||||
|
else if (linkSearchFolder != null &&
|
||||||
|
findLibrary(libraryName, linkSearchFolder) != null) {
|
||||||
log.appendMsg("Library " + libraryName + ": Already loaded ");
|
log.appendMsg("Library " + libraryName + ": Already loaded ");
|
||||||
}
|
}
|
||||||
else if (!searchPaths.isEmpty()) {
|
else if (!searchPaths.isEmpty()) {
|
||||||
|
@ -487,35 +510,45 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
for (File candidateLibraryFile : candidateLibraryFiles) {
|
for (File candidateLibraryFile : candidateLibraryFiles) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
List<String> newLibraryList = new ArrayList<>();
|
List<String> newLibraryList = new ArrayList<>();
|
||||||
Program library =
|
library = loadLibrary(simpleLibraryName, candidateLibraryFile,
|
||||||
loadLibrary(simpleLibraryName, programFolder, candidateLibraryFile,
|
|
||||||
desiredLoadSpec, newLibraryList, options, consumer, log, monitor);
|
desiredLoadSpec, newLibraryList, options, consumer, log, monitor);
|
||||||
for (String newLibraryName : newLibraryList) {
|
for (String newLibraryName : newLibraryList) {
|
||||||
unprocessed.add(new UnprocessedLibrary(newLibraryName, depth - 1));
|
unprocessed.add(new UnprocessedLibrary(newLibraryName, depth - 1));
|
||||||
}
|
}
|
||||||
if (library != null) {
|
if (library == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
foundLibrary = true;
|
foundLibrary = true;
|
||||||
|
log.appendMsg(
|
||||||
|
"Library " + libraryName + ": Examining " + candidateLibraryFile);
|
||||||
if (processLibrary(library, libraryName, candidateLibraryFile, provider,
|
if (processLibrary(library, libraryName, candidateLibraryFile, provider,
|
||||||
desiredLoadSpec, options, log, monitor)) {
|
desiredLoadSpec, options, log, monitor)) {
|
||||||
loadedPrograms.add(new LoadedProgram(library, libraryDestFolder));
|
loadedPrograms.add(
|
||||||
log.appendMsg(
|
new Loaded<Program>(library, libraryName, libraryDestFolderPath));
|
||||||
"Library " + libraryName + ": Saving " + candidateLibraryFile);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
library.release(consumer);
|
library.release(consumer);
|
||||||
log.appendMsg(
|
|
||||||
"Library " + libraryName + ": Examining " + candidateLibraryFile);
|
|
||||||
}
|
}
|
||||||
|
library = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!foundLibrary) {
|
if (!foundLibrary) {
|
||||||
log.appendMsg("Library " + libraryName + ": Not found");
|
log.appendMsg("Library " + libraryName + ": Not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
success = true;
|
||||||
return loadedPrograms;
|
return loadedPrograms;
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
if (!success) {
|
||||||
|
release(loadedPrograms, consumer);
|
||||||
|
if (library != null) {
|
||||||
|
library.release(consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the library within the specified {@link DomainFolder folder}. This method will handle
|
* Find the library within the specified {@link DomainFolder folder}. This method will handle
|
||||||
|
@ -542,7 +575,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
|
|
||||||
// Lookup by full project path
|
// Lookup by full project path
|
||||||
// NOTE: probably no need to support optional extensions and case-insensitivity for this case
|
// NOTE: probably no need to support optional extensions and case-insensitivity for this case
|
||||||
String projectPath = appendPath(folder.getPathname(), libraryPath);
|
String projectPath = concatenatePaths(folder.getPathname(), libraryPath);
|
||||||
DomainFile ret =
|
DomainFile ret =
|
||||||
folder.getProjectData().getFile(FilenameUtils.separatorsToUnix(projectPath));
|
folder.getProjectData().getFile(FilenameUtils.separatorsToUnix(projectPath));
|
||||||
if (ret != null) {
|
if (ret != null) {
|
||||||
|
@ -611,7 +644,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
|
|
||||||
// 1) Try as possible subpath under the search path
|
// 1) Try as possible subpath under the search path
|
||||||
String candidatePath =
|
String candidatePath =
|
||||||
FilenameUtils.separatorsToSystem(appendPath(searchPath, libraryPath));
|
FilenameUtils.separatorsToSystem(concatenatePaths(searchPath, libraryPath));
|
||||||
File f = resolveLibraryFile(new File(candidatePath));
|
File f = resolveLibraryFile(new File(candidatePath));
|
||||||
if (f == null || !f.isFile()) {
|
if (f == null || !f.isFile()) {
|
||||||
// 2) Fall back to looking for the library in the user specified search path, sans
|
// 2) Fall back to looking for the library in the user specified search path, sans
|
||||||
|
@ -640,9 +673,6 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* a {@link ByteProvider} available.
|
* a {@link ByteProvider} available.
|
||||||
*
|
*
|
||||||
* @param libraryName The name of the library to load
|
* @param libraryName The name of the library to load
|
||||||
* @param libraryFolder The domain folder where the new library program will be stored, if null
|
|
||||||
* the program should not be pre-saved. NOTE: the newly imported libraries will not be written
|
|
||||||
* to this folder yet, that is handled in a later follow on step.
|
|
||||||
* @param libraryFile The library file to load
|
* @param libraryFile The library file to load
|
||||||
* @param desiredLoadSpec The desired {@link LoadSpec}
|
* @param desiredLoadSpec The desired {@link LoadSpec}
|
||||||
* @param libraryNameList A {@link List} to be populated with the given library's dependent
|
* @param libraryNameList A {@link List} to be populated with the given library's dependent
|
||||||
|
@ -655,10 +685,9 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @throws CancelledException if the user cancelled the load operation
|
* @throws CancelledException if the user cancelled the load operation
|
||||||
* @throws IOException if there was an IO-related error during the load
|
* @throws IOException if there was an IO-related error during the load
|
||||||
*/
|
*/
|
||||||
private Program loadLibrary(String libraryName, DomainFolder libraryFolder, File libraryFile,
|
private Program loadLibrary(String libraryName, File libraryFile, LoadSpec desiredLoadSpec,
|
||||||
LoadSpec desiredLoadSpec, List<String> libraryNameList, List<Option> options,
|
List<String> libraryNameList, List<Option> options, Object consumer, MessageLog log,
|
||||||
Object consumer, MessageLog log, TaskMonitor monitor)
|
TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
throws CancelledException, IOException {
|
|
||||||
|
|
||||||
try (ByteProvider provider = createLibraryByteProvider(libraryFile, desiredLoadSpec, log)) {
|
try (ByteProvider provider = createLibraryByteProvider(libraryFile, desiredLoadSpec, log)) {
|
||||||
if (!shouldLoadLibrary(libraryName, libraryFile, provider, desiredLoadSpec, log)) {
|
if (!shouldLoadLibrary(libraryName, libraryFile, provider, desiredLoadSpec, log)) {
|
||||||
|
@ -671,8 +700,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Program library = doLoad(provider, libraryName, libraryFolder, libLoadSpec,
|
Program library = doLoad(provider, libraryName, libLoadSpec, libraryNameList, options,
|
||||||
libraryNameList, options, consumer, log, monitor);
|
consumer, log, monitor);
|
||||||
|
|
||||||
if (library == null) {
|
if (library == null) {
|
||||||
log.appendMsg("Library " + libraryFile + " failed to load for some reason");
|
log.appendMsg("Library " + libraryFile + " failed to load for some reason");
|
||||||
|
@ -688,7 +717,6 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
*
|
*
|
||||||
* @param provider The {@link ByteProvider} to load
|
* @param provider The {@link ByteProvider} to load
|
||||||
* @param programName The name of the new program
|
* @param programName The name of the new program
|
||||||
* @param programFolder The folder to load the program into
|
|
||||||
* @param loadSpec The {@link LoadSpec}
|
* @param loadSpec The {@link LoadSpec}
|
||||||
* @param libraryNameList A {@link List} to be populated with the loaded program's dependent
|
* @param libraryNameList A {@link List} to be populated with the loaded program's dependent
|
||||||
* library names
|
* library names
|
||||||
|
@ -700,9 +728,9 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @throws CancelledException if the user cancelled the load operation
|
* @throws CancelledException if the user cancelled the load operation
|
||||||
* @throws IOException if there was an IO-related error during the load
|
* @throws IOException if there was an IO-related error during the load
|
||||||
*/
|
*/
|
||||||
private Program doLoad(ByteProvider provider, String programName, DomainFolder programFolder,
|
private Program doLoad(ByteProvider provider, String programName, LoadSpec loadSpec,
|
||||||
LoadSpec loadSpec, List<String> libraryNameList, List<Option> options, Object consumer,
|
List<String> libraryNameList, List<Option> options, Object consumer, MessageLog log,
|
||||||
MessageLog log, TaskMonitor monitor) throws CancelledException, IOException {
|
TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
Language language = getLanguageService().getLanguage(pair.languageID);
|
Language language = getLanguageService().getLanguage(pair.languageID);
|
||||||
CompilerSpec compilerSpec = language.getCompilerSpecByID(pair.compilerSpecID);
|
CompilerSpec compilerSpec = language.getCompilerSpecByID(pair.compilerSpecID);
|
||||||
|
@ -743,7 +771,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(transactionID, success);
|
program.endTransaction(transactionID, true); // More efficient to commit when program will be discarded
|
||||||
if (!success) {
|
if (!success) {
|
||||||
program.release(consumer);
|
program.release(consumer);
|
||||||
program = null;
|
program = null;
|
||||||
|
@ -752,39 +780,35 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For each program in the given list, fix up its external library entries so that they point
|
* For each {@link Loaded} {@link Program} in the given list, fix up its external library
|
||||||
* to a path in the project.
|
* entries so that they point to a path in the project.
|
||||||
* <p>
|
* <p>
|
||||||
* Other programs in the given list are matched first, then the ghidraLibSearchFolders are
|
* Other {@link Program}s in the given list are matched first, then the given
|
||||||
* searched for matches.
|
* {@link DomainFolder search folder} is searched for matches.
|
||||||
*
|
*
|
||||||
* @param programs the list of programs to resolve against each other. Programs not saved
|
* @param loadedPrograms the list of {@link Loaded} {@link Program}s
|
||||||
* to the project will be considered as a valid external library.
|
* @param searchFolders an ordered list of {@link DomainFolder}s which imported libraries will
|
||||||
* @param searchFolder the {@link DomainFolder} which imported libraries will be searched.
|
* be searched. These folders will be searched if a library is not found within the list of
|
||||||
* This folder will be searched if a library is not found within the list of
|
* programs supplied.
|
||||||
* programs supplied. If null, only the list of programs will be considered.
|
|
||||||
* @param saveIfModified flag to have this method save any programs it modifies
|
|
||||||
* @param messageLog log for messages.
|
* @param messageLog log for messages.
|
||||||
* @param monitor the task monitor
|
* @param monitor the task monitor
|
||||||
* @throws IOException if there was an IO-related problem resolving.
|
* @throws IOException if there was an IO-related problem resolving.
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
*/
|
*/
|
||||||
private void fixupExternalLibraries(List<Program> programs, DomainFolder searchFolder,
|
private void fixupExternalLibraries(List<Loaded<Program>> loadedPrograms,
|
||||||
boolean saveIfModified, MessageLog messageLog, TaskMonitor monitor)
|
List<DomainFolder> searchFolders, MessageLog messageLog, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
|
|
||||||
Map<String, Program> progsByName = programs.stream()
|
Map<String, Loaded<Program>> loadedByName = loadedPrograms.stream()
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.collect(
|
.collect(
|
||||||
Collectors.toMap((p) -> p.getDomainFile().getName(), (p) -> p));
|
Collectors.toMap(loaded -> loaded.getName(), loaded -> loaded));
|
||||||
|
|
||||||
monitor.initialize(progsByName.size());
|
monitor.initialize(loadedByName.size());
|
||||||
for (Program program : progsByName.values()) {
|
for (Loaded<Program> loadedProgram : loadedByName.values()) {
|
||||||
monitor.incrementProgress(1);
|
monitor.incrementProgress(1);
|
||||||
if (monitor.isCancelled()) {
|
monitor.checkCanceled();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Program program = loadedProgram.getDomainObject();
|
||||||
ExternalManager extManager = program.getExternalManager();
|
ExternalManager extManager = program.getExternalManager();
|
||||||
String[] extLibNames = extManager.getExternalLibraryNames();
|
String[] extLibNames = extManager.getExternalLibraryNames();
|
||||||
if (extLibNames.length == 0 ||
|
if (extLibNames.length == 0 ||
|
||||||
|
@ -795,13 +819,10 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
monitor.setMessage("Resolving..." + program.getName());
|
monitor.setMessage("Resolving..." + program.getName());
|
||||||
int id = program.startTransaction("Resolving external references");
|
int id = program.startTransaction("Resolving external references");
|
||||||
try {
|
try {
|
||||||
resolveExternalLibraries(program, progsByName, searchFolder, monitor, messageLog);
|
resolveExternalLibraries(program, loadedByName, searchFolders, monitor, messageLog);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(id, true);
|
program.endTransaction(id, true);
|
||||||
if (saveIfModified && program.canSave() && program.isChanged()) {
|
|
||||||
program.save("Resolve external references", monitor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -812,21 +833,21 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* Other programs in the map are matched first, then the ghidraLibSearchFolders
|
* Other programs in the map are matched first, then the ghidraLibSearchFolders
|
||||||
* are searched for matches.
|
* are searched for matches.
|
||||||
*
|
*
|
||||||
* @param program the program whose Library entries are to be resolved. An open transaction
|
* @param program the program whose Library entries are to be resolved. An open
|
||||||
* on program is required.
|
* transaction on program is required.
|
||||||
* @param progsByName map of recently imported programs to be considered
|
* @param loadedByName map of recently loaded things to be considered
|
||||||
* first when resolving external Libraries. Programs not saved to the project
|
* first when resolving external Libraries. Programs not saved to the project
|
||||||
* will be ignored.
|
* will be ignored.
|
||||||
* @param searchFolder the {@link DomainFolder} which imported libraries will be searched.
|
* @param searchFolders an order list of {@link DomainFolder}s which imported libraries will be
|
||||||
* This folder will be searched if a library is not found within the list of
|
* searched. These folders will be searched if a library is not found within the list of
|
||||||
* programs supplied. If null, only the list of programs will be considered.
|
* programs supplied.
|
||||||
* @param messageLog log for messages.
|
* @param messageLog log for messages.
|
||||||
* @param monitor the task monitor
|
* @param monitor the task monitor
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
*/
|
*/
|
||||||
private void resolveExternalLibraries(Program program, Map<String, Program> progsByName,
|
private void resolveExternalLibraries(Program program,
|
||||||
DomainFolder searchFolder, TaskMonitor monitor, MessageLog messageLog)
|
Map<String, Loaded<Program>> loadedByName, List<DomainFolder> searchFolders,
|
||||||
throws CancelledException {
|
TaskMonitor monitor, MessageLog messageLog) throws CancelledException {
|
||||||
ExternalManager extManager = program.getExternalManager();
|
ExternalManager extManager = program.getExternalManager();
|
||||||
String[] extLibNames = extManager.getExternalLibraryNames();
|
String[] extLibNames = extManager.getExternalLibraryNames();
|
||||||
messageLog.appendMsg("Linking external programs to " + program.getName() + "...");
|
messageLog.appendMsg("Linking external programs to " + program.getName() + "...");
|
||||||
|
@ -837,22 +858,27 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
try {
|
try {
|
||||||
String externalFileName = FilenameUtils.getName(externalLibName);
|
String externalFileName = FilenameUtils.getName(externalLibName);
|
||||||
DomainObject matchingExtProgram = findLibrary(progsByName, externalFileName);
|
Loaded<Program> matchingExtProgram = findLibrary(loadedByName, externalFileName);
|
||||||
if (matchingExtProgram != null && matchingExtProgram.getDomainFile().exists()) {
|
if (matchingExtProgram != null) {
|
||||||
extManager.setExternalPath(externalLibName,
|
String path =
|
||||||
matchingExtProgram.getDomainFile().getPathname(), false);
|
matchingExtProgram.getProjectFolderPath() + matchingExtProgram.getName();
|
||||||
messageLog.appendMsg(" [" + externalLibName + "] -> [" +
|
extManager.setExternalPath(externalLibName, path, false);
|
||||||
matchingExtProgram.getDomainFile().getPathname() + "]");
|
messageLog.appendMsg(" [" + externalLibName + "] -> [" + path + "]");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
boolean found = false;
|
||||||
|
for (DomainFolder searchFolder : searchFolders) {
|
||||||
DomainFile alreadyImportedLib = findLibrary(externalLibName, searchFolder);
|
DomainFile alreadyImportedLib = findLibrary(externalLibName, searchFolder);
|
||||||
if (alreadyImportedLib != null) {
|
if (alreadyImportedLib != null) {
|
||||||
extManager.setExternalPath(externalLibName,
|
extManager.setExternalPath(externalLibName,
|
||||||
alreadyImportedLib.getPathname(), false);
|
alreadyImportedLib.getPathname(), false);
|
||||||
messageLog.appendMsg(" [" + externalLibName + "] -> [" +
|
messageLog.appendMsg(" [" + externalLibName + "] -> [" +
|
||||||
alreadyImportedLib.getPathname() + "] (previously imported)");
|
alreadyImportedLib.getPathname() + "] (previously imported)");
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
|
if (!found) {
|
||||||
messageLog.appendMsg(" [" + externalLibName + "] -> not found");
|
messageLog.appendMsg(" [" + externalLibName + "] -> not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -908,53 +934,26 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
/**
|
/**
|
||||||
* Find the library within the given {@link Map} of {@link Program}s
|
* Find the library within the given {@link Map} of {@link Program}s
|
||||||
*
|
*
|
||||||
* @param programsByName The map to search
|
* @param loadedByName The map to search
|
||||||
* @param libraryName The library name to lookup
|
* @param libraryName The library name to lookup
|
||||||
* @return The found {@link Program} or null if not found
|
* @return The found {@link Loaded} {@link Program} or null if not found
|
||||||
*/
|
*/
|
||||||
private Program findLibrary(Map<String, Program> programsByName, String libraryName) {
|
private Loaded<Program> findLibrary(Map<String, Loaded<Program>> loadedByName,
|
||||||
|
String libraryName) {
|
||||||
Comparator<String> comparator = getLibraryNameComparator();
|
Comparator<String> comparator = getLibraryNameComparator();
|
||||||
boolean noExtension = FilenameUtils.getExtension(libraryName).equals("");
|
boolean noExtension = FilenameUtils.getExtension(libraryName).equals("");
|
||||||
for (String key : programsByName.keySet()) {
|
for (String key : loadedByName.keySet()) {
|
||||||
String candidateName = key;
|
String candidateName = key;
|
||||||
if (isOptionalLibraryFilenameExtensions() && noExtension) {
|
if (isOptionalLibraryFilenameExtensions() && noExtension) {
|
||||||
candidateName = FilenameUtils.getBaseName(candidateName);
|
candidateName = FilenameUtils.getBaseName(candidateName);
|
||||||
}
|
}
|
||||||
if (comparator.compare(candidateName, libraryName) == 0) {
|
if (comparator.compare(candidateName, libraryName) == 0) {
|
||||||
return programsByName.get(key);
|
return loadedByName.get(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends the given path elements to form a single path
|
|
||||||
*
|
|
||||||
* @param pathElements The path elements to append to one another
|
|
||||||
* @return A single path consisting of the given path elements appended together
|
|
||||||
*/
|
|
||||||
private String appendPath(String... pathElements) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (String pathElement : pathElements) {
|
|
||||||
if (pathElement == null || pathElement.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
boolean sbEndsWithSlash =
|
|
||||||
sb.length() > 0 && "/\\".indexOf(sb.charAt(sb.length() - 1)) != -1;
|
|
||||||
boolean elementStartsWithSlash = "/\\".indexOf(pathElement.charAt(0)) != -1;
|
|
||||||
|
|
||||||
if (!sbEndsWithSlash && !elementStartsWithSlash && sb.length() > 0) {
|
|
||||||
sb.append("/");
|
|
||||||
}
|
|
||||||
else if (elementStartsWithSlash && sbEndsWithSlash) {
|
|
||||||
pathElement = pathElement.substring(1);
|
|
||||||
}
|
|
||||||
sb.append(pathElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures the given {@link LoadSpec} matches one supported by the loader
|
* Ensures the given {@link LoadSpec} matches one supported by the loader
|
||||||
*
|
*
|
||||||
|
|
|
@ -21,9 +21,11 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
|
import ghidra.app.util.OptionUtils;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -116,33 +118,33 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
|
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||||
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
|
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||||
|
throws CancelledException, IOException {
|
||||||
monitor.initialize(loadedPrograms.size());
|
monitor.initialize(loadedPrograms.size());
|
||||||
|
|
||||||
if (shouldPerformOrdinalLookup(options)) {
|
if (shouldPerformOrdinalLookup(options)) {
|
||||||
for (LoadedProgram loadedProgram : loadedPrograms) {
|
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
Program program = loadedProgram.getDomainObject();
|
||||||
Program p = loadedProgram.program();
|
int id = program.startTransaction("Ordinal fixups");
|
||||||
int id = p.startTransaction("Ordinal fixups");
|
|
||||||
boolean success = false;
|
|
||||||
try {
|
try {
|
||||||
applyLibrarySymbols(p, messageLog, monitor);
|
applyLibrarySymbols(program, messageLog, monitor);
|
||||||
applyImports(p, messageLog, monitor);
|
applyImports(program, messageLog, monitor);
|
||||||
success = true;
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
p.endTransaction(id, success);
|
program.endTransaction(id, true); // More efficient to commit when program will be discarded
|
||||||
if (p.canSave() && p.isChanged()) {
|
|
||||||
p.save("Ordinal fixups", monitor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
LibraryLookupTable.cleanup();
|
|
||||||
|
|
||||||
super.postLoadProgramFixups(loadedPrograms, options, messageLog, monitor);
|
super.postLoadProgramFixups(loadedPrograms, project, options, messageLog, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postLoadCleanup(boolean success) {
|
||||||
|
super.postLoadCleanup(success);
|
||||||
|
LibraryLookupTable.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -152,16 +154,8 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
||||||
* @return True if ordinal lookup should be performed; otherwise, false
|
* @return True if ordinal lookup should be performed; otherwise, false
|
||||||
*/
|
*/
|
||||||
private boolean shouldPerformOrdinalLookup(List<Option> options) {
|
private boolean shouldPerformOrdinalLookup(List<Option> options) {
|
||||||
boolean performOrdinalLookup = ORDINAL_LOOKUP_OPTION_DEFAULT;
|
return OptionUtils.getOption(ORDINAL_LOOKUP_OPTION_NAME, options,
|
||||||
if (options != null) {
|
ORDINAL_LOOKUP_OPTION_DEFAULT);
|
||||||
for (Option option : options) {
|
|
||||||
String optName = option.getName();
|
|
||||||
if (optName.equals(ORDINAL_LOOKUP_OPTION_NAME)) {
|
|
||||||
performOrdinalLookup = (Boolean) option.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return performOrdinalLookup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -33,13 +33,15 @@ import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.database.function.OverlappingFunctionException;
|
import ghidra.program.database.function.OverlappingFunctionException;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.FunctionManager;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.mem.InvalidAddressException;
|
import ghidra.program.model.mem.InvalidAddressException;
|
||||||
import ghidra.program.model.mem.MemoryConflictException;
|
import ghidra.program.model.mem.MemoryConflictException;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.program.util.DefaultLanguageService;
|
import ghidra.program.util.DefaultLanguageService;
|
||||||
import ghidra.program.util.GhidraProgramUtilities;
|
import ghidra.program.util.GhidraProgramUtilities;
|
||||||
import ghidra.util.*;
|
import ghidra.util.HashUtilities;
|
||||||
|
import ghidra.util.MD5Utilities;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
@ -56,34 +58,45 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
public static final String ANCHOR_LABELS_OPTION_NAME = "Anchor Processor Defined Labels";
|
public static final String ANCHOR_LABELS_OPTION_NAME = "Anchor Processor Defined Labels";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Program} with its associated {@link DomainFolder destination folder}
|
* Loads bytes in a particular format as a new {@link Loaded} {@link Program}. Multiple
|
||||||
*
|
|
||||||
* @param program The {@link Program}
|
|
||||||
* @param destinationFolder The {@link DomainFolder} where the program will get loaded to
|
|
||||||
*/
|
|
||||||
public record LoadedProgram(Program program, DomainFolder destinationFolder) {/**/}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads program bytes in a particular format as a new {@link Program}. Multiple
|
|
||||||
* {@link Program}s may end up getting created, depending on the nature of the format.
|
* {@link Program}s may end up getting created, depending on the nature of the format.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* {@link Loaded#save(Project, MessageLog, TaskMonitor)}).
|
||||||
|
* <p>
|
||||||
|
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
||||||
|
* {@link Program}s with {@link Loaded#release(Object)} when they are no longer
|
||||||
|
* needed.
|
||||||
*
|
*
|
||||||
* @param provider The bytes to load.
|
* @param provider The bytes to load.
|
||||||
* @param programName The name of the {@link Program} that's being loaded.
|
* @param loadedName A suggested name for the primary {@link Loaded} {@link Program}.
|
||||||
* @param programFolder The {@link DomainFolder} where the loaded thing should be saved. Could
|
* This is just a suggestion, and a {@link Loader} implementation reserves the right to change
|
||||||
* be null if the thing should not be pre-saved.
|
* it. The {@link Loaded} {@link Program}s should be queried for their true names using
|
||||||
|
* {@link Loaded#getName()}.
|
||||||
|
* @param project 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. Could be null if there is no project.
|
||||||
|
* @param projectFolderPath A 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}
|
||||||
|
* {@link Program}s should be queried for their true project folder paths using
|
||||||
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
* @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 log The message log.
|
* @param log The message log.
|
||||||
* @param consumer A consumer object for {@link Program}s generated.
|
* @param consumer A consumer object for generated {@link Program}s.
|
||||||
* @param monitor A cancelable task monitor.
|
* @param monitor A task monitor.
|
||||||
* @return A list of {@link LoadedProgram loaded programs} (element 0 corresponds to primary
|
* @return A {@link List} of one or more {@link Loaded} {@link Program}s (created but not
|
||||||
* loaded {@link Program}).
|
* saved).
|
||||||
|
* @throws LoadException if the load failed in an expected way.
|
||||||
* @throws IOException if there was an IO-related problem loading.
|
* @throws IOException if there was an IO-related problem loading.
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
*/
|
*/
|
||||||
protected abstract List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
protected abstract List<Loaded<Program>> loadProgram(ByteProvider provider, String loadedName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
Object consumer, TaskMonitor monitor) throws IOException, CancelledException;
|
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||||
|
throws IOException, LoadException, CancelledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads program bytes into the specified {@link Program}. This method will not create any new
|
* Loads program bytes into the specified {@link Program}. This method will not create any new
|
||||||
|
@ -97,109 +110,66 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
* @param messageLog The message log.
|
* @param messageLog The message log.
|
||||||
* @param program The {@link Program} to load into.
|
* @param program The {@link Program} to load into.
|
||||||
* @param monitor A cancelable task monitor.
|
* @param monitor A cancelable task monitor.
|
||||||
* @return True if the file was successfully loaded; otherwise, false.
|
* @throws LoadException if the load failed in an expected way.
|
||||||
* @throws IOException if there was an IO-related problem loading.
|
* @throws IOException if there was an IO-related problem loading.
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
*/
|
*/
|
||||||
protected abstract boolean loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
protected abstract void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||||
List<Option> options, MessageLog messageLog, Program program, TaskMonitor monitor)
|
List<Option> options, MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException;
|
throws IOException, LoadException, CancelledException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final List<DomainObject> load(ByteProvider provider, String name, DomainFolder folder,
|
public final LoadResults<? extends DomainObject> load(ByteProvider provider, String loadedName,
|
||||||
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, Object consumer,
|
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
TaskMonitor monitor) throws IOException, CancelledException, InvalidNameException,
|
MessageLog messageLog, Object consumer, TaskMonitor monitor) throws IOException,
|
||||||
DuplicateNameException, VersionException {
|
CancelledException, VersionException, LoadException {
|
||||||
|
|
||||||
if (!isOverrideMainProgramName()) {
|
|
||||||
folder = ProjectDataUtils.createDomainFolderPath(folder, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<DomainObject> results = new ArrayList<>();
|
|
||||||
|
|
||||||
if (!loadSpec.isComplete()) {
|
if (!loadSpec.isComplete()) {
|
||||||
return results;
|
throw new LoadException("Load spec is incomplete");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<LoadedProgram> loadedPrograms =
|
List<Loaded<Program>> loadedPrograms = loadProgram(provider, loadedName, project,
|
||||||
loadProgram(provider, name, folder, loadSpec, options, messageLog, consumer, monitor);
|
projectFolderPath, loadSpec, options, messageLog, consumer, monitor);
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
|
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
for (LoadedProgram loadedProgram : loadedPrograms) {
|
Program program = loadedProgram.getDomainObject();
|
||||||
monitor.checkCanceled();
|
|
||||||
|
|
||||||
Program program = loadedProgram.program();
|
|
||||||
|
|
||||||
applyProcessorLabels(options, program);
|
applyProcessorLabels(options, program);
|
||||||
|
|
||||||
program.setEventsEnabled(true);
|
program.setEventsEnabled(true);
|
||||||
|
|
||||||
// TODO: null should not be used as a determinant for saving; don't allow null
|
|
||||||
// folders?
|
|
||||||
if (loadedProgram.destinationFolder() == null) {
|
|
||||||
results.add(program);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String domainFileName = program.getName();
|
|
||||||
if (isOverrideMainProgramName()) {
|
|
||||||
// If this is the main imported program, use the given name, otherwise, use the
|
|
||||||
// internal program name. The first program in the list is the main imported program
|
|
||||||
if (program == loadedPrograms.get(0).program()) {
|
|
||||||
domainFileName = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (createProgramFile(program, loadedProgram.destinationFolder(), domainFileName,
|
|
||||||
messageLog, monitor)) {
|
|
||||||
results.add(program);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
program.release(consumer); // some kind of exception happened; see MessageLog
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subclasses can perform custom post-load fix-ups
|
// Subclasses can perform custom post-load fix-ups
|
||||||
postLoadProgramFixups(loadedPrograms, options, messageLog, monitor);
|
postLoadProgramFixups(loadedPrograms, project, options, messageLog, monitor);
|
||||||
|
|
||||||
success = true;
|
success = true;
|
||||||
|
return new LoadResults<Program>(loadedPrograms);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedPrograms, consumer);
|
release(loadedPrograms, consumer);
|
||||||
}
|
}
|
||||||
|
postLoadCleanup(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some loaders can return more than one program.
|
|
||||||
* This method indicates whether the first (or main) program's name
|
|
||||||
* should be overridden and changed to the imported file name.
|
|
||||||
* @return true if first program name should be changed
|
|
||||||
*/
|
|
||||||
protected boolean isOverrideMainProgramName() {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
public final void loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||||
MessageLog messageLog, Program program, TaskMonitor monitor)
|
MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, LoadException, CancelledException {
|
||||||
|
|
||||||
if (!loadSpec.isComplete()) {
|
if (!loadSpec.isComplete()) {
|
||||||
return false;
|
throw new LoadException("Load spec is incomplete");
|
||||||
}
|
}
|
||||||
|
|
||||||
program.setEventsEnabled(false);
|
program.setEventsEnabled(false);
|
||||||
int transactionID = program.startTransaction("Loading - " + getName());
|
int transactionID = program.startTransaction("Loading - " + getName());
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
success = loadProgramInto(provider, loadSpec, options, messageLog, program, monitor);
|
loadProgramInto(provider, loadSpec, options, messageLog, program, monitor);
|
||||||
return success;
|
success = true;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(transactionID, success);
|
program.endTransaction(transactionID, success);
|
||||||
|
@ -237,19 +207,35 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This gets called after the given list of {@link LoadedProgram programs}s is finished loading.
|
* This gets called after the given list of {@link Loaded loaded programs}s is finished loading.
|
||||||
* It provides subclasses an opportunity to do follow-on actions to the load.
|
* It provides subclasses an opportunity to do follow-on actions to the load.
|
||||||
*
|
*
|
||||||
* @param loadedPrograms The {@link LoadedProgram programs} that got loaded.
|
* @param loadedPrograms The {@link Loaded loaded programs} to be fixed up.
|
||||||
|
* @param project The {@link Project} to load into. Could be null if there is no project.
|
||||||
* @param options The load options.
|
* @param options The load options.
|
||||||
* @param messageLog The message log.
|
* @param messageLog The message log.
|
||||||
* @param monitor A cancelable task monitor.
|
* @param monitor A cancelable task monitor.
|
||||||
* @throws IOException if there was an IO-related problem loading.
|
* @throws IOException if there was an IO-related problem loading.
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
*/
|
*/
|
||||||
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
|
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||||
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
|
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||||
// Default behavior is to do nothing.
|
throws CancelledException, IOException {
|
||||||
|
// Default behavior is to do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This gets called as the final step of the load process. Subclasses may override it to ensure
|
||||||
|
* any resources they created can be cleaned up after the load finishes.
|
||||||
|
* <p>
|
||||||
|
* NOTE: Subclasses should not use this method to release any {@link Program}s they created when
|
||||||
|
* failure occurs. That should be done by the subclass as soon as it detects failure has
|
||||||
|
* occurred.
|
||||||
|
*
|
||||||
|
* @param success True if the load completed successfully; otherwise, false
|
||||||
|
*/
|
||||||
|
protected void postLoadCleanup(boolean success) {
|
||||||
|
// Default behavior is to do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -262,6 +248,35 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenates the given path elements to form a single path. Empty and null path elements
|
||||||
|
* are ignored.
|
||||||
|
*
|
||||||
|
* @param pathElements The path elements to append to one another
|
||||||
|
* @return A single path consisting of the given path elements appended together
|
||||||
|
*/
|
||||||
|
protected String concatenatePaths(String... pathElements) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String pathElement : pathElements) {
|
||||||
|
if (pathElement == null || pathElement.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean sbEndsWithSlash =
|
||||||
|
sb.length() > 0 && "/\\".indexOf(sb.charAt(sb.length() - 1)) != -1;
|
||||||
|
boolean elementStartsWithSlash = "/\\".indexOf(pathElement.charAt(0)) != -1;
|
||||||
|
|
||||||
|
if (!sbEndsWithSlash && !elementStartsWithSlash && sb.length() > 0) {
|
||||||
|
sb.append("/");
|
||||||
|
}
|
||||||
|
else if (elementStartsWithSlash && sbEndsWithSlash) {
|
||||||
|
pathElement = pathElement.substring(1);
|
||||||
|
}
|
||||||
|
sb.append(pathElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a block name.
|
* Generates a block name.
|
||||||
*
|
*
|
||||||
|
@ -307,25 +322,27 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
Program prog = new ProgramDB(programName, language, compilerSpec, consumer);
|
Program prog = new ProgramDB(programName, language, compilerSpec, consumer);
|
||||||
prog.setEventsEnabled(false);
|
prog.setEventsEnabled(false);
|
||||||
int id = prog.startTransaction("Set program properties");
|
int id = prog.startTransaction("Set program properties");
|
||||||
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
setProgramProperties(prog, provider, executableFormatName);
|
setProgramProperties(prog, provider, executableFormatName);
|
||||||
|
|
||||||
if (shouldSetImageBase(prog, imageBase)) {
|
|
||||||
try {
|
try {
|
||||||
|
if (shouldSetImageBase(prog, imageBase)) {
|
||||||
prog.setImageBase(imageBase, true);
|
prog.setImageBase(imageBase, true);
|
||||||
}
|
}
|
||||||
catch (AddressOverflowException e) {
|
success = true;
|
||||||
// can't happen here
|
return prog;
|
||||||
}
|
|
||||||
catch (LockException e) {
|
|
||||||
// can't happen here
|
|
||||||
}
|
}
|
||||||
|
catch (LockException | AddressOverflowException e) {
|
||||||
|
// shouldn't ever happen here
|
||||||
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
prog.endTransaction(id, true);
|
prog.endTransaction(id, true); // More efficient to commit when program will be discarded
|
||||||
|
if (!success) {
|
||||||
|
prog.release(consumer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return prog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -453,62 +470,17 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Releases the given consumer from each of the provided {@link LoadedProgram}s.
|
* Releases the given consumer from each of the provided {@link Loaded loaded programs}
|
||||||
*
|
*
|
||||||
* @param loadedPrograms A list of {@link LoadedProgram}s which are no longer being used.
|
* @param loadedPrograms A list of {@link Loaded loaded programs} which are no longer being used
|
||||||
* @param consumer The consumer that was marking the {@link DomainObject}s as being used.
|
* @param consumer The consumer that was marking the {@link Program}s as being used
|
||||||
*/
|
*/
|
||||||
protected final void release(List<LoadedProgram> loadedPrograms, Object consumer) {
|
protected final void release(List<Loaded<Program>> loadedPrograms, Object consumer) {
|
||||||
for (LoadedProgram loadedProgram : loadedPrograms) {
|
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
||||||
loadedProgram.program().release(consumer);
|
loadedProgram.getDomainObject().release(consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean createProgramFile(Program program, DomainFolder programFolder,
|
|
||||||
String programName, MessageLog messageLog, TaskMonitor monitor)
|
|
||||||
throws CancelledException, InvalidNameException {
|
|
||||||
|
|
||||||
int uniqueNameIndex = 0;
|
|
||||||
String uniqueName = programName;
|
|
||||||
while (!monitor.isCancelled()) {
|
|
||||||
try {
|
|
||||||
programFolder.createFile(uniqueName, program, monitor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (DuplicateFileException e) {
|
|
||||||
uniqueName = programName + uniqueNameIndex;
|
|
||||||
++uniqueNameIndex;
|
|
||||||
}
|
|
||||||
catch (CancelledException | InvalidNameException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
Throwable t = e.getCause();
|
|
||||||
if (t == null) {
|
|
||||||
t = e;
|
|
||||||
}
|
|
||||||
String msg = t.getMessage();
|
|
||||||
if (msg == null) {
|
|
||||||
msg = "";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
msg = "\n" + msg;
|
|
||||||
}
|
|
||||||
Msg.showError(this, null, "Create Program Failed",
|
|
||||||
"Failed to create program file: " + uniqueName + msg, e);
|
|
||||||
messageLog.appendMsg("Unexpected exception creating file: " + uniqueName);
|
|
||||||
messageLog.appendException(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// makes the data tree expand to show new file!
|
|
||||||
// The following line was disabled as it causes UI updates that are better
|
|
||||||
// done by the callers to this loader instead of this loader.
|
|
||||||
//programFolder.setActive();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
|
|
|
@ -21,7 +21,7 @@ import java.util.List;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.model.DomainFolder;
|
import ghidra.framework.model.Project;
|
||||||
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.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -52,9 +52,10 @@ public abstract class AbstractProgramWrapperLoader extends AbstractProgramLoader
|
||||||
throws CancelledException, IOException;
|
throws CancelledException, IOException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
Object consumer, TaskMonitor monitor) throws CancelledException, IOException {
|
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
Language language = getLanguageService().getLanguage(pair.languageID);
|
Language language = getLanguageService().getLanguage(pair.languageID);
|
||||||
|
@ -66,6 +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.of(new Loaded<Program>(program, programName, programFolderPath));
|
||||||
|
|
||||||
int transactionID = program.startTransaction("Loading");
|
int transactionID = program.startTransaction("Loading");
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
|
@ -73,31 +76,31 @@ public abstract class AbstractProgramWrapperLoader extends AbstractProgramLoader
|
||||||
load(provider, loadSpec, options, program, monitor, log);
|
load(provider, loadSpec, options, program, monitor, log);
|
||||||
createDefaultMemoryBlocks(program, language, log);
|
createDefaultMemoryBlocks(program, language, log);
|
||||||
success = true;
|
success = true;
|
||||||
return List.of(new LoadedProgram(program, programFolder));
|
return loadedList;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(transactionID, success);
|
program.endTransaction(transactionID, true); // More efficient to commit when program will be discarded
|
||||||
if (!success) {
|
if (!success) {
|
||||||
program.release(consumer);
|
release(loadedList, consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||||
List<Option> options, MessageLog log, Program program, TaskMonitor monitor)
|
List<Option> options, MessageLog log, Program program, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, LoadException, IOException {
|
||||||
|
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
LanguageID languageID = program.getLanguageID();
|
LanguageID languageID = program.getLanguageID();
|
||||||
CompilerSpecID compilerSpecID = program.getCompilerSpec().getCompilerSpecID();
|
CompilerSpecID compilerSpecID = program.getCompilerSpec().getCompilerSpecID();
|
||||||
if (!(pair.languageID.equals(languageID) && pair.compilerSpecID.equals(compilerSpecID))) {
|
if (!(pair.languageID.equals(languageID) && pair.compilerSpecID.equals(compilerSpecID))) {
|
||||||
log.appendMsg(provider.getAbsolutePath() +
|
String message = provider.getAbsolutePath() +
|
||||||
" does not have the same language/compiler spec as program " + program.getName());
|
" does not have the same language/compiler spec as program " + program.getName();
|
||||||
return false;
|
log.appendMsg(message);
|
||||||
|
throw new LoadException(message);
|
||||||
}
|
}
|
||||||
load(provider, loadSpec, options, program, monitor, log);
|
load(provider, loadSpec, options, program, monitor, log);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -21,8 +21,8 @@ import java.util.*;
|
||||||
import ghidra.app.util.*;
|
import ghidra.app.util.*;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.model.DomainFolder;
|
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
import ghidra.program.database.mem.FileBytes;
|
import ghidra.program.database.mem.FileBytes;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
|
@ -269,9 +269,10 @@ public class BinaryLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||||
CompilerSpec importerCompilerSpec =
|
CompilerSpec importerCompilerSpec =
|
||||||
|
@ -281,30 +282,27 @@ public class BinaryLoader extends AbstractProgramLoader {
|
||||||
importerLanguage.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
importerLanguage.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||||
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.of(new Loaded<>(prog, programName, programFolderPath));
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
success = loadInto(provider, loadSpec, options, log, prog, monitor);
|
loadInto(provider, loadSpec, options, log, prog, monitor);
|
||||||
if (success) {
|
|
||||||
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
||||||
}
|
success = true;
|
||||||
|
return loadedList;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
prog.release(consumer);
|
release(loadedList, consumer);
|
||||||
prog = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<LoadedProgram> results = new ArrayList<>();
|
|
||||||
if (prog != null) {
|
|
||||||
results.add(new LoadedProgram(prog, programFolder));
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||||
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, LoadException, CancelledException {
|
||||||
long length = getLength(options);
|
long length = getLength(options);
|
||||||
//File file = provider.getFile();
|
//File file = provider.getFile();
|
||||||
long fileOffset = getFileOffset(options);
|
long fileOffset = getFileOffset(options);
|
||||||
|
@ -329,12 +327,10 @@ public class BinaryLoader extends AbstractProgramLoader {
|
||||||
blockName = generateBlockName(prog, isOverlay, baseAddr.getAddressSpace());
|
blockName = generateBlockName(prog, isOverlay, baseAddr.getAddressSpace());
|
||||||
}
|
}
|
||||||
createBlock(prog, isOverlay, blockName, baseAddr, fileBytes, length, log);
|
createBlock(prog, isOverlay, blockName, baseAddr, fileBytes, length, log);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
catch (AddressOverflowException e) {
|
catch (AddressOverflowException e) {
|
||||||
throw new IllegalArgumentException("Invalid address range specified: start:" +
|
throw new LoadException("Invalid address range specified: start:" + baseAddr +
|
||||||
baseAddr + ", length:" + length + " - end address exceeds address space boundary!");
|
", length:" + length + " - end address exceeds address space boundary!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -176,22 +176,16 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
|
||||||
|
|
||||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||||
|
|
||||||
int id = program.startTransaction("loading program from COFF");
|
|
||||||
boolean success = false;
|
|
||||||
try {
|
try {
|
||||||
processSectionHeaders(provider, header, program, fileBytes, monitor, log, sectionsMap,
|
processSectionHeaders(provider, header, program, fileBytes, monitor, log, sectionsMap,
|
||||||
performFakeLinking);
|
performFakeLinking);
|
||||||
processSymbols(header, program, monitor, log, sectionsMap, symbolsMap);
|
processSymbols(header, program, monitor, log, sectionsMap, symbolsMap);
|
||||||
processEntryPoint(header, program, monitor, log);
|
processEntryPoint(header, program, monitor, log);
|
||||||
processRelocations(header, program, sectionsMap, symbolsMap, log, monitor);
|
processRelocations(header, program, sectionsMap, symbolsMap, log, monitor);
|
||||||
success = true;
|
|
||||||
}
|
}
|
||||||
catch (AddressOverflowException e) {
|
catch (AddressOverflowException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
program.endTransaction(id, success);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processEntryPoint(CoffFileHeader header, Program program, TaskMonitor monitor,
|
private void processEntryPoint(CoffFileHeader header, Program program, TaskMonitor monitor,
|
||||||
|
|
|
@ -24,6 +24,7 @@ import ghidra.app.util.bin.format.elf.ElfException;
|
||||||
import ghidra.app.util.bin.format.elf.ElfHeader;
|
import ghidra.app.util.bin.format.elf.ElfHeader;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.lang.Endian;
|
import ghidra.program.model.lang.Endian;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -152,14 +153,12 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<LoadedProgram> loadedPrograms, List<Option> options,
|
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||||
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
|
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||||
super.postLoadProgramFixups(loadedPrograms, options, messageLog, monitor);
|
throws CancelledException, IOException {
|
||||||
|
super.postLoadProgramFixups(loadedPrograms, project, options, messageLog, monitor);
|
||||||
|
|
||||||
for (LoadedProgram loadedProgram : loadedPrograms) {
|
ELFExternalSymbolResolver.fixUnresolvedExternalSymbols(loadedPrograms, messageLog, monitor);
|
||||||
ELFExternalSymbolResolver.fixUnresolvedExternalSymbols(loadedProgram.program(), true,
|
|
||||||
messageLog, monitor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,17 +20,25 @@ import java.util.*;
|
||||||
|
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
|
import db.DBConstants;
|
||||||
|
import db.DBHandle;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.DomainObject;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
|
import ghidra.framework.store.db.PackedDatabase;
|
||||||
import ghidra.framework.store.local.ItemSerializer;
|
import ghidra.framework.store.local.ItemSerializer;
|
||||||
|
import ghidra.program.database.DataTypeArchiveContentHandler;
|
||||||
|
import ghidra.program.database.DataTypeArchiveDB;
|
||||||
import ghidra.program.model.data.FileDataTypeManager;
|
import ghidra.program.model.data.FileDataTypeManager;
|
||||||
|
import ghidra.program.model.lang.LanguageNotFoundException;
|
||||||
import ghidra.program.model.listing.DataTypeArchive;
|
import ghidra.program.model.listing.DataTypeArchive;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
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;
|
||||||
|
import utilities.util.FileUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a packed Ghidra data type archive.
|
* Loads a packed Ghidra data type archive.
|
||||||
|
@ -44,48 +52,66 @@ public class GdtLoader implements Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DomainObject> load(ByteProvider provider, String filename,
|
public LoadResults<? extends DomainObject> load(ByteProvider provider, String filename,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options,
|
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
MessageLog messageLog, Object consumer, TaskMonitor monitor) throws IOException,
|
MessageLog messageLog, Object consumer, TaskMonitor monitor) throws IOException,
|
||||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
CancelledException, VersionException {
|
||||||
|
|
||||||
DomainFile df = doImport(provider, filename, programFolder, monitor);
|
DataTypeArchive dtArchive =
|
||||||
|
loadPackedProgramDatabase(provider, filename, consumer, monitor);
|
||||||
monitor.setMessage("Opening " + filename);
|
return new LoadResults<>(dtArchive, filename, projectFolderPath);
|
||||||
// Allow upgrade since imported project archives must always be upgraded
|
|
||||||
DomainObject dobj = df.getDomainObject(consumer, true, false, monitor);
|
|
||||||
if (!(dobj instanceof DataTypeArchive)) {
|
|
||||||
if (dobj != null) {
|
|
||||||
dobj.release(consumer);
|
|
||||||
df.delete();
|
|
||||||
}
|
|
||||||
throw new IOException("File imported is not a Data Type Archive: " + filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DomainObject> results = new ArrayList<DomainObject>();
|
private DataTypeArchive loadPackedProgramDatabase(ByteProvider provider, String programName,
|
||||||
results.add(dobj);
|
Object consumer, TaskMonitor monitor)
|
||||||
return results;
|
throws IOException, CancelledException, VersionException, LanguageNotFoundException {
|
||||||
}
|
DataTypeArchive dtArchive;
|
||||||
|
|
||||||
private DomainFile doImport(ByteProvider provider, String filename,
|
|
||||||
DomainFolder programFolder, TaskMonitor monitor)
|
|
||||||
throws InvalidNameException, CancelledException, IOException {
|
|
||||||
|
|
||||||
File file = provider.getFile();
|
File file = provider.getFile();
|
||||||
DomainFolder folder = programFolder;
|
File tmpFile = null;
|
||||||
|
if (file == null) {
|
||||||
|
file = tmpFile = createTmpFile(provider, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
monitor.setMessage("Restoring " + file.getName());
|
try {
|
||||||
|
PackedDatabase packedDatabase = PackedDatabase.getPackedDatabase(file, true, monitor);
|
||||||
|
boolean success = false;
|
||||||
|
DBHandle dbh = null;
|
||||||
|
try {
|
||||||
|
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE.equals(
|
||||||
|
packedDatabase.getContentType())) {
|
||||||
|
throw new IOException("File imported is not a Program: " + programName);
|
||||||
|
}
|
||||||
|
|
||||||
DomainFile df = folder.createFile(filename, file, monitor);
|
monitor.setMessage("Restoring " + provider.getName());
|
||||||
|
|
||||||
return df;
|
dbh = packedDatabase.open(monitor);
|
||||||
|
dtArchive = new DataTypeArchiveDB(dbh, DBConstants.UPGRADE, monitor, consumer);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (!success) {
|
||||||
|
if (dbh != null) {
|
||||||
|
dbh.close(); // also disposes packed database object
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
packedDatabase.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dtArchive;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (tmpFile != null) {
|
||||||
|
tmpFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
public void loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||||
MessageLog messageLog, Program program, TaskMonitor monitor)
|
MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, LoadException, CancelledException {
|
||||||
throw new UnsupportedOperationException("cannot add GDT to program");
|
throw new LoadException("Cannot add GDT to program");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -110,6 +136,16 @@ public class GdtLoader implements Loader {
|
||||||
return FilenameUtils.removeExtension(provider.getName());
|
return FilenameUtils.removeExtension(provider.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static File createTmpFile(ByteProvider provider, TaskMonitor monitor)
|
||||||
|
throws IOException {
|
||||||
|
File tmpFile = File.createTempFile("ghidra_gdt_loader", null);
|
||||||
|
try (InputStream is = provider.getInputStream(0);
|
||||||
|
FileOutputStream fos = new FileOutputStream(tmpFile)) {
|
||||||
|
FileUtilities.copyStreamToStream(is, fos, monitor);
|
||||||
|
}
|
||||||
|
return tmpFile;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isGDTFile(ByteProvider provider) {
|
private static boolean isGDTFile(ByteProvider provider) {
|
||||||
if (!provider.getName().toLowerCase().endsWith(FileDataTypeManager.SUFFIX)) {
|
if (!provider.getName().toLowerCase().endsWith(FileDataTypeManager.SUFFIX)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -15,9 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.opinion;
|
package ghidra.app.util.opinion;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
|
@ -26,15 +25,16 @@ import db.DBHandle;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.DomainObject;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
import ghidra.framework.store.db.PackedDatabase;
|
import ghidra.framework.store.db.PackedDatabase;
|
||||||
import ghidra.framework.store.local.ItemSerializer;
|
import ghidra.framework.store.local.ItemSerializer;
|
||||||
import ghidra.program.database.ProgramContentHandler;
|
import ghidra.program.database.ProgramContentHandler;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.model.lang.LanguageNotFoundException;
|
import ghidra.program.model.lang.LanguageNotFoundException;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
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;
|
||||||
import utilities.util.FileUtilities;
|
import utilities.util.FileUtilities;
|
||||||
|
|
||||||
|
@ -70,42 +70,19 @@ public class GzfLoader implements Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DomainObject> load(ByteProvider provider, String programName,
|
public LoadResults<? extends DomainObject> load(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options,
|
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
MessageLog messageLog, Object consumer, TaskMonitor monitor) throws IOException,
|
MessageLog messageLog, Object consumer, TaskMonitor monitor) throws IOException,
|
||||||
CancelledException, DuplicateNameException, InvalidNameException, VersionException {
|
CancelledException, VersionException {
|
||||||
|
|
||||||
DomainObject dobj;
|
Program program = loadPackedProgramDatabase(provider, programName, consumer, monitor);
|
||||||
if (programFolder == null) {
|
return new LoadResults<>(program, programName, projectFolderPath);
|
||||||
dobj = loadPackedProgramDatabase(provider, programName, consumer, monitor);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DomainFile df = doLoad(provider, programName, programFolder, monitor);
|
|
||||||
boolean success = false;
|
|
||||||
try {
|
|
||||||
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(df.getContentType())) {
|
|
||||||
throw new IOException("File imported is not a Program: " + programName);
|
|
||||||
}
|
|
||||||
monitor.setMessage("Opening " + programName);
|
|
||||||
dobj = df.getDomainObject(consumer, true, false, monitor);
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (!success) {
|
|
||||||
df.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DomainObject> results = new ArrayList<>();
|
private Program loadPackedProgramDatabase(ByteProvider provider, String programName,
|
||||||
results.add(dobj);
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DomainObject loadPackedProgramDatabase(ByteProvider provider, String programName,
|
|
||||||
Object consumer, TaskMonitor monitor)
|
Object consumer, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException, VersionException, LanguageNotFoundException {
|
throws IOException, CancelledException, VersionException, LanguageNotFoundException {
|
||||||
DomainObject dobj;
|
Program program;
|
||||||
File file = provider.getFile();
|
File file = provider.getFile();
|
||||||
File tmpFile = null;
|
File tmpFile = null;
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
|
@ -125,7 +102,7 @@ public class GzfLoader implements Loader {
|
||||||
monitor.setMessage("Restoring " + provider.getName());
|
monitor.setMessage("Restoring " + provider.getName());
|
||||||
|
|
||||||
dbh = packedDatabase.open(monitor);
|
dbh = packedDatabase.open(monitor);
|
||||||
dobj = new ProgramDB(dbh, DBConstants.UPGRADE, monitor, consumer);
|
program = new ProgramDB(dbh, DBConstants.UPGRADE, monitor, consumer);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
@ -138,7 +115,7 @@ public class GzfLoader implements Loader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dobj;
|
return program;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (tmpFile != null) {
|
if (tmpFile != null) {
|
||||||
|
@ -148,10 +125,10 @@ public class GzfLoader implements Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
public void loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||||
MessageLog messageLog, Program program, TaskMonitor monitor)
|
MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, LoadException, CancelledException {
|
||||||
throw new UnsupportedOperationException("cannot add GZF to program");
|
throw new LoadException("Cannot add GZF to program");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -169,30 +146,6 @@ public class GzfLoader implements Loader {
|
||||||
return FilenameUtils.removeExtension(provider.getName());
|
return FilenameUtils.removeExtension(provider.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private DomainFile doLoad(ByteProvider provider, String programName,
|
|
||||||
DomainFolder programFolder, TaskMonitor monitor)
|
|
||||||
throws InvalidNameException, CancelledException, IOException {
|
|
||||||
|
|
||||||
File file = provider.getFile();
|
|
||||||
File tmpFile = null;
|
|
||||||
if (file == null) {
|
|
||||||
file = tmpFile = createTmpFile(provider, monitor);
|
|
||||||
}
|
|
||||||
DomainFolder folder = programFolder;
|
|
||||||
|
|
||||||
monitor.setMessage("Restoring " + provider.getName());
|
|
||||||
|
|
||||||
try {
|
|
||||||
DomainFile df = folder.createFile(programName, file, monitor);
|
|
||||||
return df;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (tmpFile != null) {
|
|
||||||
tmpFile.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File createTmpFile(ByteProvider provider, TaskMonitor monitor)
|
private static File createTmpFile(ByteProvider provider, TaskMonitor monitor)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
File tmpFile = File.createTempFile("ghidra_gzf_loader", null);
|
File tmpFile = File.createTempFile("ghidra_gzf_loader", null);
|
||||||
|
|
|
@ -21,8 +21,8 @@ import java.util.*;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.model.DomainFolder;
|
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -142,9 +142,10 @@ public class IntelHexLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||||
CompilerSpec importerCompilerSpec =
|
CompilerSpec importerCompilerSpec =
|
||||||
|
@ -152,45 +153,38 @@ 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.of(new Loaded<>(prog, programName, programFolderPath));
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
success = loadInto(provider, loadSpec, options, log, prog, monitor);
|
loadInto(provider, loadSpec, options, log, prog, monitor);
|
||||||
if (success) {
|
|
||||||
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
||||||
}
|
success = true;
|
||||||
|
return loadedList;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
prog.release(consumer);
|
release(loadedList, consumer);
|
||||||
prog = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<LoadedProgram> results = new ArrayList<>();
|
|
||||||
if (prog != null) {
|
|
||||||
results.add(new LoadedProgram(prog, programFolder));
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||||
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, LoadException, CancelledException {
|
||||||
Address baseAddr = getBaseAddr(options);
|
Address baseAddr = getBaseAddr(options);
|
||||||
|
|
||||||
if (baseAddr == null) {
|
if (baseAddr == null) {
|
||||||
baseAddr = prog.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
baseAddr = prog.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||||
}
|
}
|
||||||
boolean success = false;
|
|
||||||
try {
|
try {
|
||||||
processIntelHex(provider, options, log, prog, monitor);
|
processIntelHex(provider, options, log, prog, monitor);
|
||||||
success = true;
|
|
||||||
}
|
}
|
||||||
catch (AddressOverflowException e) {
|
catch (AddressOverflowException e) {
|
||||||
throw new IOException(
|
throw new LoadException(
|
||||||
"Hex file specifies range greater than allowed address space - " + e.getMessage());
|
"Hex file specifies range greater than allowed address space - " + e.getMessage());
|
||||||
}
|
}
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processIntelHex(ByteProvider provider, List<Option> options, MessageLog log,
|
private void processIntelHex(ByteProvider provider, List<Option> options, MessageLog log,
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/* ###
|
||||||
|
* 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 java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a {@link Loader#load(ByteProvider, String, Project, String, LoadSpec, List,MessageLog, Object, TaskMonitor) load}
|
||||||
|
* fails in an expected way. The supplied message should explain the reason.
|
||||||
|
*/
|
||||||
|
public class LoadException extends IOException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link LoadException} with the given message
|
||||||
|
*
|
||||||
|
* @param message The exception message
|
||||||
|
*/
|
||||||
|
public LoadException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
/* ###
|
||||||
|
* 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 java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.framework.model.*;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a
|
||||||
|
* {@link Loader#load(ghidra.app.util.bin.ByteProvider, String, Project, String, LoadSpec, List, MessageLog, Object, TaskMonitor) load}.
|
||||||
|
* A {@link LoadResults} object provides convenient access to and operations on the underlying
|
||||||
|
* {@link Loaded} {@link DomainObject}s that got loaded.
|
||||||
|
*
|
||||||
|
* @param <T> The type of {@link DomainObject}s that were loaded
|
||||||
|
*/
|
||||||
|
public class LoadResults<T extends DomainObject> implements Iterable<Loaded<T>> {
|
||||||
|
|
||||||
|
private final List<Loaded<T>> loadedList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* the {@link #getPrimary() primary} {@link Loaded} {@link DomainObject}.
|
||||||
|
*
|
||||||
|
* @param loadedList A {@link List} of {@link Loaded} {@link DomainObject}s
|
||||||
|
* @throws IllegalArgumentException if the provided {@link List} is null or empty
|
||||||
|
*/
|
||||||
|
public LoadResults(List<Loaded<T>> loadedList) throws IllegalArgumentException {
|
||||||
|
if (loadedList == null || loadedList.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("The loaded list must not be empty");
|
||||||
|
}
|
||||||
|
this.loadedList = new ArrayList<>(loadedList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a a new {@link LoadResults} that contains a new {@link Loaded}
|
||||||
|
* {@link DomainObject} created from the given parameters. This new {@link Loaded}
|
||||||
|
* {@link DomainObject} is assumed to be the {@link #getPrimary() primary} {@link Loaded}
|
||||||
|
* {@link DomainObject}.
|
||||||
|
*
|
||||||
|
* @param domainObject The loaded {@link DomainObject}
|
||||||
|
* @param name The name of the loaded {@link DomainObject}. If a
|
||||||
|
* {@link #save(Project, Object, MessageLog, TaskMonitor) save} occurs, this will attempted to
|
||||||
|
* be used for the resulting {@link DomainFile}'s name.
|
||||||
|
* @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,
|
||||||
|
* the root project folder will be used.
|
||||||
|
*/
|
||||||
|
public LoadResults(T domainObject, String name, String projectFolderPath) {
|
||||||
|
this(List.of(new Loaded<T>(domainObject, name, projectFolderPath)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the "primary" {@link Loaded} {@link DomainObject}, who's meaning is defined by each
|
||||||
|
* {@link Loader} implementation
|
||||||
|
*
|
||||||
|
* @return The "primary" {@link Loaded} {@link DomainObject}
|
||||||
|
*/
|
||||||
|
public Loaded<T> getPrimary() {
|
||||||
|
return loadedList.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the "primary" {@link DomainObject}, who's meaning is defined by each {@link Loader}
|
||||||
|
* implementation
|
||||||
|
*
|
||||||
|
* @return The "primary" {@link DomainObject}
|
||||||
|
*/
|
||||||
|
public T getPrimaryDomainObject() {
|
||||||
|
return loadedList.get(0).getDomainObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of {@link Loaded} {@link DomainObject}s in this {@link LoadResults}. The
|
||||||
|
* size will always be greater than 0.
|
||||||
|
*
|
||||||
|
* @return The number of {@link Loaded} {@link DomainObject}s in this {@link LoadResults}
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return loadedList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Loaded#save(Project, MessageLog, TaskMonitor) Saves} each {@link Loaded}
|
||||||
|
* {@link DomainObject} to the given {@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
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws IOException If there was a problem saving
|
||||||
|
* @see Loaded#save(Project, MessageLog, TaskMonitor)
|
||||||
|
*/
|
||||||
|
public void save(Project project, Object consumer, MessageLog messageLog, TaskMonitor monitor)
|
||||||
|
throws CancelledException, IOException {
|
||||||
|
boolean success = false;
|
||||||
|
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
|
||||||
|
* longer using them. When the last consumer invokes this method, the {@link Loaded}
|
||||||
|
* {@link DomainObject}s will be closed and will become invalid.
|
||||||
|
*
|
||||||
|
* @param consumer the consumer
|
||||||
|
*/
|
||||||
|
public void release(Object consumer) {
|
||||||
|
loadedList.forEach(loaded -> loaded.release(consumer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the filtered {@link Loaded} {@link DomainObject}s that the specified consumer is no
|
||||||
|
* longer using them. When the last consumer invokes this method, the filtered {@link Loaded}
|
||||||
|
* {@link DomainObject}s will be closed and will become invalid.
|
||||||
|
*
|
||||||
|
* @param consumer the consumer
|
||||||
|
* @param filter a filter to apply to the {@link Loaded} {@link DomainObject}s prior to the
|
||||||
|
* release
|
||||||
|
*/
|
||||||
|
public void release(Object consumer, Predicate<? super Loaded<T>> filter) {
|
||||||
|
loadedList.stream().filter(filter).forEach(loaded -> loaded.release(consumer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the non-primary {@link Loaded} {@link DomainObject}s that the specified consumer is no
|
||||||
|
* longer using them. When the last consumer invokes this method, the non-primary {@link Loaded}
|
||||||
|
* {@link DomainObject}s will be closed and will become invalid.
|
||||||
|
*
|
||||||
|
* @param consumer the consumer
|
||||||
|
*/
|
||||||
|
public void releaseNonPrimary(Object consumer) {
|
||||||
|
for (int i = 0; i < loadedList.size(); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
loadedList.get(i).release(consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Loaded<T>> iterator() {
|
||||||
|
return loadedList.iterator();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,221 @@
|
||||||
|
/* ###
|
||||||
|
* 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 java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.framework.model.*;
|
||||||
|
import ghidra.util.InvalidNameException;
|
||||||
|
import ghidra.util.exception.*;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* for the loaded {@link DomainObject}, should it get saved to a project.
|
||||||
|
*
|
||||||
|
* @param <T> The type of {@link DomainObject} that was loaded
|
||||||
|
*/
|
||||||
|
public class Loaded<T extends DomainObject> {
|
||||||
|
|
||||||
|
private final T domainObject;
|
||||||
|
private final String name;
|
||||||
|
private String projectFolderPath;
|
||||||
|
|
||||||
|
private DomainFile domainFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link Loaded} object
|
||||||
|
*
|
||||||
|
* @param domainObject The loaded {@link DomainObject}
|
||||||
|
* @param name The name of the loaded {@link DomainObject}. If a
|
||||||
|
* {@link #save(Project, MessageLog, TaskMonitor)} occurs, this will attempted to be used for
|
||||||
|
* the resulting {@link DomainFile}'s name.
|
||||||
|
* @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
|
||||||
|
* project folder will be used.
|
||||||
|
*/
|
||||||
|
public Loaded(T domainObject, String name, String projectFolderPath) {
|
||||||
|
this.domainObject = domainObject;
|
||||||
|
this.name = name;
|
||||||
|
setProjectFolderPath(projectFolderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the loaded {@link DomainObject}
|
||||||
|
*
|
||||||
|
* @return The loaded {@link DomainObject}
|
||||||
|
*/
|
||||||
|
public T getDomainObject() {
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of the loaded {@link DomainObject}. If a
|
||||||
|
* {@link #save(Project, MessageLog, TaskMonitor)} occurs, this will attempted to be used for
|
||||||
|
* the resulting {@link DomainFile}'s name.
|
||||||
|
*
|
||||||
|
* @return the name of the loaded {@link DomainObject}
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the project folder path this will get saved to during a
|
||||||
|
* {@link #save(Project, MessageLog, TaskMonitor)} operation.
|
||||||
|
* <p>
|
||||||
|
* NOTE: The returned path will always end with a "/".
|
||||||
|
*
|
||||||
|
* @return the project folder path
|
||||||
|
*/
|
||||||
|
public String getProjectFolderPath() {
|
||||||
|
return projectFolderPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the project folder path this will get saved to during a
|
||||||
|
* {@link #save(Project, MessageLog, TaskMonitor)} operation.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* project folder will be used.
|
||||||
|
*/
|
||||||
|
public void setProjectFolderPath(String projectFolderPath) {
|
||||||
|
if (projectFolderPath == null) {
|
||||||
|
projectFolderPath = "/";
|
||||||
|
}
|
||||||
|
else if (!projectFolderPath.endsWith("/")) {
|
||||||
|
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
|
||||||
|
* project folder path, using this object's name.
|
||||||
|
* <p>
|
||||||
|
* If a {@link DomainFile} already exists with the same desired name and project folder path,
|
||||||
|
* the desired name will get a counter value appended to it to avoid a naming conflict.
|
||||||
|
* Therefore, it should not be assumed that the returned {@link DomainFile} will have the same
|
||||||
|
* 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
|
||||||
|
* @return The {@link DomainFile} where the save happened
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
* @throws ClosedException if the loaded {@link DomainObject} was already closed
|
||||||
|
* @throws IOException If there was an IO-related error, an invalid name was specified, or it
|
||||||
|
* was already successfully saved and still exists
|
||||||
|
*/
|
||||||
|
public DomainFile save(Project project, MessageLog messageLog, TaskMonitor monitor)
|
||||||
|
throws CancelledException, ClosedException, IOException {
|
||||||
|
|
||||||
|
if (domainObject.isClosed()) {
|
||||||
|
throw new ClosedException(
|
||||||
|
"Cannot saved closed DomainObject: " + domainObject.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (getSavedDomainFile() != null) { //
|
||||||
|
throw new IOException("Already saved to " + domainFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e) {
|
||||||
|
// DomainFile was already saved, but no longer exists.
|
||||||
|
// Allow the save to proceeded.
|
||||||
|
domainFile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uniqueNameIndex = 0;
|
||||||
|
String uniqueName = name;
|
||||||
|
try {
|
||||||
|
DomainFolder programFolder = ProjectDataUtils.createDomainFolderPath(
|
||||||
|
project.getProjectData().getRootFolder(), projectFolderPath);
|
||||||
|
while (!monitor.isCancelled()) {
|
||||||
|
try {
|
||||||
|
domainFile = programFolder.createFile(uniqueName, domainObject, monitor);
|
||||||
|
return domainFile;
|
||||||
|
}
|
||||||
|
catch (DuplicateFileException e) {
|
||||||
|
uniqueName = name + "." + uniqueNameIndex;
|
||||||
|
++uniqueNameIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InvalidNameException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
throw new CancelledException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the loaded {@link DomainObject}'s associated {@link DomainFile} that was
|
||||||
|
* {@link #save(Project, MessageLog, TaskMonitor) saved}
|
||||||
|
*
|
||||||
|
* @return The loaded {@link DomainObject}'s associated saved {@link DomainFile}, or null if
|
||||||
|
* was not saved
|
||||||
|
* @throws FileNotFoundException If the loaded {@link DomainObject} was saved but the associated
|
||||||
|
* {@link DomainFile} no longer exists
|
||||||
|
* @see #save(Project, MessageLog, TaskMonitor)
|
||||||
|
*/
|
||||||
|
public DomainFile getSavedDomainFile() throws FileNotFoundException {
|
||||||
|
if (domainFile != null && !domainFile.exists()) {
|
||||||
|
throw new FileNotFoundException("Saved DomainFile no longer exists: " + domainFile);
|
||||||
|
}
|
||||||
|
return domainFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @throws IOException If there was an issue deleting the saved {@link DomainFile}
|
||||||
|
* @see #save(Project, MessageLog, TaskMonitor)
|
||||||
|
*/
|
||||||
|
void deleteSavedDomainFile(Object consumer) throws IOException {
|
||||||
|
if (domainFile != null && domainFile.exists()) {
|
||||||
|
domainFile.delete();
|
||||||
|
domainFile = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getProjectFolderPath() + getName();
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,13 +23,12 @@ import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.formats.gfilesystem.FSRL;
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
import ghidra.framework.model.DomainFolder;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.model.DomainObject;
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.InvalidNameException;
|
|
||||||
import ghidra.util.classfinder.ClassSearcher;
|
import ghidra.util.classfinder.ClassSearcher;
|
||||||
import ghidra.util.classfinder.ExtensionPoint;
|
import ghidra.util.classfinder.ExtensionPoint;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.exception.VersionException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,8 +40,17 @@ import ghidra.util.task.TaskMonitor;
|
||||||
*/
|
*/
|
||||||
public interface Loader extends ExtensionPoint, Comparable<Loader> {
|
public interface Loader extends ExtensionPoint, Comparable<Loader> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string prefixed to each loader headless command line argument to avoid naming conflicts
|
||||||
|
* with other headless command line argument names
|
||||||
|
*/
|
||||||
public static final String COMMAND_LINE_ARG_PREFIX = "-loader";
|
public static final String COMMAND_LINE_ARG_PREFIX = "-loader";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key used to lookup and store all loader options in the project's saved state
|
||||||
|
*/
|
||||||
|
public static final String OPTIONS_PROJECT_SAVE_STATE_KEY = "LOADER_OPTIONS";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this {@link Loader} supports loading the given {@link ByteProvider}, this methods returns
|
* If this {@link Loader} supports loading the given {@link ByteProvider}, this methods returns
|
||||||
* a {@link Collection} of all supported {@link LoadSpec}s that contain discovered load
|
* a {@link Collection} of all supported {@link LoadSpec}s that contain discovered load
|
||||||
|
@ -59,33 +67,50 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
|
||||||
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException;
|
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads bytes in a particular format as a new {@link DomainObject}.
|
* Loads bytes in a particular format as a new {@link Loaded} {@link DomainObject}. Multiple
|
||||||
* Multiple {@link DomainObject}s may end up getting created, depending on the nature of the
|
* {@link DomainObject}s may end up getting created, depending on the nature of the format.
|
||||||
* format.
|
* The {@link Loaded} {@link DomainObject}s are bundled together in a {@link LoadResults}
|
||||||
|
* object which provides convenience methods to operate on the entire group of {@link Loaded}
|
||||||
|
* {@link DomainObject}s.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* {@link LoadResults#save(Project, Object, MessageLog, TaskMonitor)}).
|
||||||
|
* <p>
|
||||||
|
* It is also the responsibility of the caller to release the returned {@link Loaded}
|
||||||
|
* {@link DomainObject}s with {@link LoadResults#release(Object)} when they are no longer
|
||||||
|
* needed.
|
||||||
*
|
*
|
||||||
* @param provider The bytes to load.
|
* @param provider The bytes to load.
|
||||||
* @param name The name of the thing that's being loaded.
|
* @param loadedName A suggested name for the primary {@link Loaded} {@link DomainObject}.
|
||||||
* @param folder The {@link DomainFolder} where the loaded thing should be saved. Could be
|
* This is just a suggestion, and a {@link Loader} implementation reserves the right to change
|
||||||
* null if the thing should not be pre-saved.
|
* it. The {@link LoadResults} should be queried for their true names using
|
||||||
|
* {@link Loaded#getName()}.
|
||||||
|
* @param project 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. Could be null if there is no project.
|
||||||
|
* @param projectFolderPath A suggested project folder path for the {@link Loaded}
|
||||||
|
* {@link DomainObject}s. This is just a suggestion, and a {@link Loader} implementation
|
||||||
|
* reserves the right to change it for each {@link Loaded} result. The {@link LoadResults}
|
||||||
|
* should be queried for their true project folder paths using
|
||||||
|
* {@link Loaded#getProjectFolderPath()}.
|
||||||
* @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 {@link DomainObject} generated.
|
* @param consumer A consumer object for generated {@link DomainObject}s.
|
||||||
* @param monitor A cancelable task monitor.
|
* @param monitor A task monitor.
|
||||||
* @return A list of loaded {@link DomainObject}s (element 0 corresponds to primary loaded
|
* @return The {@link LoadResults} which contains one or more {@link Loaded}
|
||||||
* object).
|
* {@link DomainObject}s (created but not saved).
|
||||||
|
* @throws LoadException if the load failed in an expected way
|
||||||
* @throws IOException if there was an IO-related problem loading.
|
* @throws IOException if there was an IO-related problem loading.
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
* @throws DuplicateNameException if the load resulted in a naming conflict with the
|
* @throws VersionException if the load process tried to open an existing {@link DomainFile}
|
||||||
* {@link DomainObject}.
|
* which was created with a newer or unsupported version of Ghidra
|
||||||
* @throws InvalidNameException if an invalid {@link DomainObject} name was used during load.
|
|
||||||
* @throws VersionException if there was an issue with database versions, probably due to a
|
|
||||||
* failed language upgrade.
|
|
||||||
*/
|
*/
|
||||||
public List<DomainObject> load(ByteProvider provider, String name, DomainFolder folder,
|
public LoadResults<? extends DomainObject> load(ByteProvider provider, String loadedName,
|
||||||
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, Object consumer,
|
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
TaskMonitor monitor) throws IOException, CancelledException, DuplicateNameException,
|
MessageLog messageLog, Object consumer, TaskMonitor monitor) throws IOException,
|
||||||
InvalidNameException, VersionException;
|
CancelledException, VersionException, LoadException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads bytes into the specified {@link Program}. This method will not create any new
|
* Loads bytes into the specified {@link Program}. This method will not create any new
|
||||||
|
@ -97,13 +122,13 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
|
||||||
* @param messageLog The message log.
|
* @param messageLog The message log.
|
||||||
* @param program The {@link Program} to load into.
|
* @param program The {@link Program} to load into.
|
||||||
* @param monitor A cancelable task monitor.
|
* @param monitor A cancelable task monitor.
|
||||||
* @return True if the file was successfully loaded; otherwise, false.
|
* @throws LoadException if the load failed in an expected way.
|
||||||
* @throws IOException if there was an IO-related problem loading.
|
* @throws IOException if there was an IO-related problem loading.
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
*/
|
*/
|
||||||
public boolean loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
public void loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||||
MessageLog messageLog, Program program, TaskMonitor monitor)
|
MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException;
|
throws IOException, LoadException, CancelledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the default {@link Loader} options.
|
* Gets the default {@link Loader} options.
|
||||||
|
@ -187,6 +212,17 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if this {@link Loader} loads into a new {@link DomainFolder} instead of a new
|
||||||
|
* {@link DomainFile}
|
||||||
|
*
|
||||||
|
* @return True if this {@link Loader} loads into a new {@link DomainFolder} instead of a new
|
||||||
|
* {@link DomainFile}
|
||||||
|
*/
|
||||||
|
public default boolean loadsIntoNewFolder() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public default int compareTo(Loader o) {
|
public default int compareTo(Loader o) {
|
||||||
int compareTiers = getTier().compareTo(o.getTier());
|
int compareTiers = getTier().compareTo(o.getTier());
|
||||||
|
|
|
@ -22,8 +22,8 @@ import ghidra.app.util.MemoryBlockUtils;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.model.DomainFolder;
|
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -160,9 +160,10 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||||
|
throws IOException, LoadException, CancelledException {
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||||
CompilerSpec importerCompilerSpec =
|
CompilerSpec importerCompilerSpec =
|
||||||
|
@ -170,45 +171,38 @@ 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.of(new Loaded<>(prog, programName, programFolderPath));
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
success = loadInto(provider, loadSpec, options, log, prog, monitor);
|
loadInto(provider, loadSpec, options, log, prog, monitor);
|
||||||
if (success) {
|
|
||||||
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
||||||
}
|
success = true;
|
||||||
|
return loadedList;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
prog.release(consumer);
|
release(loadedList, consumer);
|
||||||
prog = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<LoadedProgram> results = new ArrayList<>();
|
|
||||||
if (prog != null) {
|
|
||||||
results.add(new LoadedProgram(prog, programFolder));
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||||
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, LoadException, CancelledException {
|
||||||
Address baseAddr = getBaseAddr(options);
|
Address baseAddr = getBaseAddr(options);
|
||||||
|
|
||||||
if (baseAddr == null) {
|
if (baseAddr == null) {
|
||||||
baseAddr = prog.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
baseAddr = prog.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||||
}
|
}
|
||||||
boolean success = false;
|
|
||||||
try {
|
try {
|
||||||
processMotorolaHex(provider, options, prog, baseAddr, monitor);
|
processMotorolaHex(provider, options, prog, baseAddr, monitor);
|
||||||
success = true;
|
|
||||||
}
|
}
|
||||||
catch (AddressOverflowException e) {
|
catch (AddressOverflowException e) {
|
||||||
throw new IOException(
|
throw new LoadException(
|
||||||
"Hex file specifies range greater than allowed address space - " + e.getMessage());
|
"Hex file specifies range greater than allowed address space - " + e.getMessage());
|
||||||
}
|
}
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processMotorolaHex(ByteProvider provider, List<Option> options, Program program,
|
private void processMotorolaHex(ByteProvider provider, List<Option> options, Program program,
|
||||||
|
|
|
@ -132,21 +132,15 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
|
||||||
// the original bytes.
|
// the original bytes.
|
||||||
MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||||
|
|
||||||
int id = program.startTransaction("loading program from OMF");
|
|
||||||
boolean success = false;
|
|
||||||
try {
|
try {
|
||||||
processSegmentHeaders(reader, header, program, monitor, log);
|
processSegmentHeaders(reader, header, program, monitor, log);
|
||||||
processExternalSymbols(header, program, monitor, log);
|
processExternalSymbols(header, program, monitor, log);
|
||||||
processPublicSymbols(header, program, monitor, log);
|
processPublicSymbols(header, program, monitor, log);
|
||||||
processRelocations(header, program, monitor, log);
|
processRelocations(header, program, monitor, log);
|
||||||
success = true;
|
|
||||||
}
|
}
|
||||||
catch (AddressOverflowException e) {
|
catch (AddressOverflowException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
program.endTransaction(id, success);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,8 +29,8 @@ import ghidra.app.util.OptionException;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.xml.*;
|
import ghidra.app.util.xml.*;
|
||||||
import ghidra.framework.model.DomainFolder;
|
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
|
import ghidra.framework.model.Project;
|
||||||
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.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -177,10 +177,11 @@ public class XmlLoader extends AbstractProgramLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
|
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||||
List<LoadedProgram> results = new ArrayList<>();
|
throws IOException, LoadException, CancelledException {
|
||||||
|
List<Loaded<Program>> results = new ArrayList<>();
|
||||||
|
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||||
|
@ -198,36 +199,35 @@ 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.of(new Loaded<>(prog, programName, programFolderPath));
|
||||||
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);
|
||||||
if (success) {
|
if (success) {
|
||||||
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
||||||
|
return loadedList;
|
||||||
}
|
}
|
||||||
|
throw new LoadException("Failed to load");
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
prog.release(consumer);
|
release(loadedList, consumer);
|
||||||
prog = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (prog != null) {
|
|
||||||
results.add(new LoadedProgram(prog, programFolder));
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||||
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, LoadException, CancelledException {
|
||||||
File file = provider.getFile();
|
File file = provider.getFile();
|
||||||
return doImport(new ProgramXmlMgr(file), options, log, prog, monitor, true);
|
doImport(new ProgramXmlMgr(file), options, log, prog, monitor, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doImportWork(final ProgramXmlMgr mgr, final List<Option> options,
|
private boolean doImportWork(final ProgramXmlMgr mgr, final List<Option> options,
|
||||||
final MessageLog log, Program prog, TaskMonitor monitor,
|
final MessageLog log, Program prog, TaskMonitor monitor,
|
||||||
final boolean isAddToProgram) throws IOException {
|
final boolean isAddToProgram) throws LoadException {
|
||||||
MessageLog mgrLog = null;
|
MessageLog mgrLog = null;
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
|
@ -247,7 +247,7 @@ public class XmlLoader extends AbstractProgramLoader {
|
||||||
message = log.toString();
|
message = log.toString();
|
||||||
}
|
}
|
||||||
Msg.warn(this, "XML import exception, log: " + message, e);
|
Msg.warn(this, "XML import exception, log: " + message, e);
|
||||||
throw new IOException(e.getMessage(), e);
|
throw new LoadException(e.getMessage());
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,12 @@ package ghidra.base.project;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.nio.channels.OverlappingFileLockException;
|
import java.nio.channels.OverlappingFileLockException;
|
||||||
import java.util.*;
|
import java.util.HashMap;
|
||||||
|
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.*;
|
||||||
import ghidra.app.util.opinion.Loader;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.app.util.opinion.LoaderService;
|
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
import ghidra.framework.client.*;
|
import ghidra.framework.client.*;
|
||||||
import ghidra.framework.cmd.Command;
|
import ghidra.framework.cmd.Command;
|
||||||
|
@ -612,8 +612,10 @@ public class GhidraProject {
|
||||||
CompilerSpec compilerSpec) throws CancelledException, DuplicateNameException,
|
CompilerSpec compilerSpec) throws CancelledException, DuplicateNameException,
|
||||||
InvalidNameException, VersionException, IOException {
|
InvalidNameException, VersionException, IOException {
|
||||||
MessageLog messageLog = new MessageLog();
|
MessageLog messageLog = new MessageLog();
|
||||||
Program program = AutoImporter.importByLookingForLcs(file, domainFolder, language,
|
LoadResults<Program> loadResults = AutoImporter.importByLookingForLcs(file, project,
|
||||||
compilerSpec, this, messageLog, MONITOR);
|
domainFolder.getPathname(), language, compilerSpec, this, messageLog, MONITOR);
|
||||||
|
Program program = loadResults.getPrimaryDomainObject();
|
||||||
|
loadResults.releaseNonPrimary(this);
|
||||||
initializeProgram(program, false);
|
initializeProgram(program, false);
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
@ -630,8 +632,10 @@ public class GhidraProject {
|
||||||
throws CancelledException, DuplicateNameException, InvalidNameException,
|
throws CancelledException, DuplicateNameException, InvalidNameException,
|
||||||
VersionException, IOException {
|
VersionException, IOException {
|
||||||
MessageLog messageLog = new MessageLog();
|
MessageLog messageLog = new MessageLog();
|
||||||
Program program = AutoImporter.importByUsingSpecificLoaderClass(file, null, loaderClass,
|
LoadResults<Program> loadResults = AutoImporter.importByUsingSpecificLoaderClass(file,
|
||||||
null, this, messageLog, MONITOR);
|
project, null, loaderClass, null, this, messageLog, MONITOR);
|
||||||
|
Program program = loadResults.getPrimaryDomainObject();
|
||||||
|
loadResults.releaseNonPrimary(this);
|
||||||
initializeProgram(program, false);
|
initializeProgram(program, false);
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
@ -642,13 +646,11 @@ public class GhidraProject {
|
||||||
MessageLog messageLog = new MessageLog();
|
MessageLog messageLog = new MessageLog();
|
||||||
SingleLoaderFilter loaderFilter = new SingleLoaderFilter(loaderClass, null);
|
SingleLoaderFilter loaderFilter = new SingleLoaderFilter(loaderClass, null);
|
||||||
LcsHintLoadSpecChooser opinionChoose = new LcsHintLoadSpecChooser(language, compilerSpec);
|
LcsHintLoadSpecChooser opinionChoose = new LcsHintLoadSpecChooser(language, compilerSpec);
|
||||||
List<Program> programs = AutoImporter.importFresh(file, null, this, messageLog, MONITOR,
|
LoadResults<Program> loadResults =
|
||||||
loaderFilter, opinionChoose, null, new LoaderArgsOptionChooser(loaderFilter),
|
AutoImporter.importFresh(file, project, null, this, messageLog, MONITOR, loaderFilter,
|
||||||
MultipleProgramsStrategy.ONE_PROGRAM_OR_NULL);
|
opinionChoose, null, new LoaderArgsOptionChooser(loaderFilter));
|
||||||
if (programs != null && programs.size() == 1) {
|
loadResults.releaseNonPrimary(this);
|
||||||
return programs.get(0);
|
return loadResults.getPrimaryDomainObject();
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program importProgram(File file) throws CancelledException, DuplicateNameException,
|
public Program importProgram(File file) throws CancelledException, DuplicateNameException,
|
||||||
|
@ -659,8 +661,10 @@ public class GhidraProject {
|
||||||
public Program importProgram(File file, DomainFolder domainFolder) throws CancelledException,
|
public Program importProgram(File file, DomainFolder domainFolder) throws CancelledException,
|
||||||
DuplicateNameException, InvalidNameException, VersionException, IOException {
|
DuplicateNameException, InvalidNameException, VersionException, IOException {
|
||||||
MessageLog messageLog = new MessageLog();
|
MessageLog messageLog = new MessageLog();
|
||||||
Program program =
|
LoadResults<Program> loadResults = AutoImporter.importByUsingBestGuess(file, project,
|
||||||
AutoImporter.importByUsingBestGuess(file, domainFolder, this, messageLog, MONITOR);
|
domainFolder.getPathname(), this, messageLog, MONITOR);
|
||||||
|
Program program = loadResults.getPrimaryDomainObject();
|
||||||
|
loadResults.releaseNonPrimary(this);
|
||||||
initializeProgram(program, false);
|
initializeProgram(program, false);
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
@ -696,14 +700,12 @@ public class GhidraProject {
|
||||||
MessageLog messageLog = new MessageLog();
|
MessageLog messageLog = new MessageLog();
|
||||||
|
|
||||||
String programNameOverride = null;
|
String programNameOverride = null;
|
||||||
List<Program> programs = AutoImporter.importFresh(file, null, this, messageLog, MONITOR,
|
LoadResults<Program> loadResults =
|
||||||
|
AutoImporter.importFresh(file, project, null, this, messageLog, MONITOR,
|
||||||
LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED,
|
LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED,
|
||||||
programNameOverride, OptionChooser.DEFAULT_OPTIONS,
|
programNameOverride, OptionChooser.DEFAULT_OPTIONS);
|
||||||
MultipleProgramsStrategy.ONE_PROGRAM_OR_NULL);
|
loadResults.releaseNonPrimary(this);
|
||||||
if (programs != null && programs.size() == 1) {
|
return loadResults.getPrimaryDomainObject();
|
||||||
return programs.get(0);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
|
@ -328,7 +328,10 @@ public class DataTreeDialog extends DialogComponentProvider
|
||||||
* @return null if there was no domain folder selected
|
* @return null if there was no domain folder selected
|
||||||
*/
|
*/
|
||||||
public DomainFolder getDomainFolder() {
|
public DomainFolder getDomainFolder() {
|
||||||
if (domainFolder == null && !cancelled) {
|
if (cancelled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (domainFolder == null) {
|
||||||
domainFolder = treePanel.getSelectedDomainFolder();
|
domainFolder = treePanel.getSelectedDomainFolder();
|
||||||
}
|
}
|
||||||
return domainFolder;
|
return domainFolder;
|
||||||
|
|
|
@ -53,13 +53,13 @@ public class AddToProgramDialog extends ImporterDialog {
|
||||||
super("Add To Program: " + fsrl.getPath(), tool, loaderMap, byteProvider, null);
|
super("Add To Program: " + fsrl.getPath(), tool, loaderMap, byteProvider, null);
|
||||||
this.addToProgram = addToProgram;
|
this.addToProgram = addToProgram;
|
||||||
folderNameTextField.setText(getFolderName(addToProgram));
|
folderNameTextField.setText(getFolderName(addToProgram));
|
||||||
filenameTextField.setText(addToProgram.getName());
|
nameTextField.setText(addToProgram.getName());
|
||||||
setSelectedLanguage(getLanguageSpec());
|
setSelectedLanguage(getLanguageSpec());
|
||||||
languageTextField.setEnabled(false);
|
languageTextField.setEnabled(false);
|
||||||
folderNameTextField.setEnabled(false);
|
folderNameTextField.setEnabled(false);
|
||||||
folderButton.setEnabled(false);
|
folderButton.setEnabled(false);
|
||||||
languageButton.setEnabled(false);
|
languageButton.setEnabled(false);
|
||||||
filenameTextField.setEnabled(false);
|
nameTextField.setEnabled(false);
|
||||||
validateFormInput();
|
validateFormInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class ImporterDialog extends DialogComponentProvider {
|
||||||
private String suggestedDestinationPath;
|
private String suggestedDestinationPath;
|
||||||
|
|
||||||
protected ByteProvider byteProvider;
|
protected ByteProvider byteProvider;
|
||||||
protected JTextField filenameTextField;
|
protected JTextField nameTextField;
|
||||||
private boolean userHasChangedName;
|
private boolean userHasChangedName;
|
||||||
protected JButton folderButton;
|
protected JButton folderButton;
|
||||||
protected JButton languageButton;
|
protected JButton languageButton;
|
||||||
|
@ -90,7 +90,7 @@ public class ImporterDialog extends DialogComponentProvider {
|
||||||
* @param byteProvider the ByteProvider for getting the bytes from the file to be imported. The
|
* @param byteProvider the ByteProvider for getting the bytes from the file to be imported. The
|
||||||
* dialog takes ownership of the ByteProvider and it will be closed when the dialog is closed
|
* dialog takes ownership of the ByteProvider and it will be closed when the dialog is closed
|
||||||
* @param suggestedDestinationPath optional string path that will be pre-pended to the destination
|
* @param suggestedDestinationPath optional string path that will be pre-pended to the destination
|
||||||
* filename. Any path specified in the destination filename field will be created when
|
* name. Any path specified in the destination name field will be created when
|
||||||
* the user performs the import (as opposed to the {@link #setDestinationFolder(DomainFolder) destination folder}
|
* the user performs the import (as opposed to the {@link #setDestinationFolder(DomainFolder) destination folder}
|
||||||
* option which requires the DomainFolder to already exist). The two destination paths work together
|
* option which requires the DomainFolder to already exist). The two destination paths work together
|
||||||
* to specify the final Ghidra project folder where the imported binary is placed.
|
* to specify the final Ghidra project folder where the imported binary is placed.
|
||||||
|
@ -160,26 +160,26 @@ public class ImporterDialog extends DialogComponentProvider {
|
||||||
panel.add(new GLabel("Destination Folder: ", SwingConstants.RIGHT));
|
panel.add(new GLabel("Destination Folder: ", SwingConstants.RIGHT));
|
||||||
panel.add(buildFolderPanel());
|
panel.add(buildFolderPanel());
|
||||||
panel.add(new GLabel("Program Name: ", SwingConstants.RIGHT));
|
panel.add(new GLabel("Program Name: ", SwingConstants.RIGHT));
|
||||||
panel.add(buildFilenameTextField());
|
panel.add(buildNameTextField());
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component buildFilenameTextField() {
|
private Component buildNameTextField() {
|
||||||
String initalSuggestedFilename =
|
String initalSuggestedName =
|
||||||
FSUtilities.appendPath(suggestedDestinationPath, getSuggestedFilename());
|
FSUtilities.appendPath(suggestedDestinationPath, getSuggestedName());
|
||||||
int columns = (initalSuggestedFilename.length() > 50) ? 50 : 0;
|
int columns = (initalSuggestedName.length() > 50) ? 50 : 0;
|
||||||
filenameTextField = new JTextField(initalSuggestedFilename, columns);
|
nameTextField = new JTextField(initalSuggestedName, columns);
|
||||||
|
|
||||||
// Use a key listener to track users edits. We can't use the document listener, as
|
// Use a key listener to track users edits. We can't use the document listener, as
|
||||||
// we change the name field ourselves when other fields are changed.
|
// we change the name field ourselves when other fields are changed.
|
||||||
filenameTextField.addKeyListener(new KeyAdapter() {
|
nameTextField.addKeyListener(new KeyAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void keyTyped(KeyEvent e) {
|
public void keyTyped(KeyEvent e) {
|
||||||
// tracking all key events; are there any that we don't want to track?
|
// tracking all key events; are there any that we don't want to track?
|
||||||
userHasChangedName = true;
|
userHasChangedName = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
filenameTextField.getDocument().addDocumentListener(new DocumentListener() {
|
nameTextField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
@Override
|
@Override
|
||||||
public void changedUpdate(DocumentEvent e) {
|
public void changedUpdate(DocumentEvent e) {
|
||||||
// don't care
|
// don't care
|
||||||
|
@ -195,10 +195,10 @@ public class ImporterDialog extends DialogComponentProvider {
|
||||||
validateFormInput();
|
validateFormInput();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return filenameTextField;
|
return nameTextField;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSuggestedFilename() {
|
private String getSuggestedName() {
|
||||||
Loader loader = getSelectedLoader();
|
Loader loader = getSelectedLoader();
|
||||||
if (loader != null) {
|
if (loader != null) {
|
||||||
return loader.getPreferredFileName(byteProvider);
|
return loader.getPreferredFileName(byteProvider);
|
||||||
|
@ -302,9 +302,9 @@ public class ImporterDialog extends DialogComponentProvider {
|
||||||
if (loader != null) {
|
if (loader != null) {
|
||||||
languageNeeded = isLanguageNeeded(loader);
|
languageNeeded = isLanguageNeeded(loader);
|
||||||
setSelectedLanguage(getPreferredLanguage(loader));
|
setSelectedLanguage(getPreferredLanguage(loader));
|
||||||
String newSuggestedFilename =
|
String newSuggestedName =
|
||||||
FSUtilities.appendPath(suggestedDestinationPath, getSuggestedFilename());
|
FSUtilities.appendPath(suggestedDestinationPath, getSuggestedName());
|
||||||
setFilename(newSuggestedFilename);
|
setName(newSuggestedName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
languageNeeded = true;
|
languageNeeded = true;
|
||||||
|
@ -459,7 +459,7 @@ public class ImporterDialog extends DialogComponentProvider {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
optionsButton.setEnabled(selectedLanguage != null);
|
optionsButton.setEnabled(selectedLanguage != null);
|
||||||
if (!validateFilename()) {
|
if (!validateName()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setStatusText("");
|
setStatusText("");
|
||||||
|
@ -467,64 +467,67 @@ public class ImporterDialog extends DialogComponentProvider {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateFilename() {
|
private boolean validateName() {
|
||||||
|
Loader loader = getSelectedLoader();
|
||||||
|
boolean loadsIntoFolder = loader.loadsIntoNewFolder();
|
||||||
|
String destType = loadsIntoFolder ? "folder" : "file name";
|
||||||
if (getName().isEmpty()) {
|
if (getName().isEmpty()) {
|
||||||
setStatusText("Please enter a destination file name.");
|
setStatusText("Please enter a destination " + destType + ".");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (warnedAboutInvalidFilenameChars()) {
|
if (warnedAboutInvalidNameChars()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isMissingFilename()) {
|
if (isMissingName()) {
|
||||||
setStatusText("Destination path does not specify filename.");
|
setStatusText("Destination path does not specify " + destType + ".");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isDuplicateFilename()) {
|
if (isDuplicateName(loadsIntoFolder)) {
|
||||||
setStatusText("Destination file name already exists.");
|
setStatusText("Destination " + destType + " already exists.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isFilenameTooLong()) {
|
if (isNameTooLong()) {
|
||||||
setStatusText("Destination file name is too long. ( >" +
|
setStatusText("Destination " + destType + " is too long. ( >" +
|
||||||
tool.getProject().getProjectData().getMaxNameLength() + ")");
|
tool.getProject().getProjectData().getMaxNameLength() + ")");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean warnedAboutInvalidFilenameChars() {
|
private boolean warnedAboutInvalidNameChars() {
|
||||||
String filename = getName();
|
String name = getName();
|
||||||
for (int i = 0; i < filename.length(); i++) {
|
for (int i = 0; i < name.length(); i++) {
|
||||||
char ch = filename.charAt(i);
|
char ch = name.charAt(i);
|
||||||
if (!LocalFileSystem.isValidNameCharacter(ch) && ch != '/') {
|
if (!LocalFileSystem.isValidNameCharacter(ch) && ch != '/') {
|
||||||
setStatusText("Invalid character " + ch + " in filename.");
|
setStatusText("Invalid character " + ch + " in name.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMissingFilename() {
|
private boolean isMissingName() {
|
||||||
String filename = FilenameUtils.getName(getName());
|
String name = FilenameUtils.getName(getName());
|
||||||
return StringUtils.isBlank(filename);
|
return StringUtils.isBlank(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDuplicateFilename() {
|
private boolean isDuplicateName(boolean isFolder) {
|
||||||
String pathFilename = getName();
|
String pathName = getName();
|
||||||
String parentPath = FilenameUtils.getFullPathNoEndSeparator(pathFilename);
|
String parentPath = FilenameUtils.getFullPathNoEndSeparator(pathName);
|
||||||
String filename = FilenameUtils.getName(pathFilename);
|
String fileOrFolderName = FilenameUtils.getName(pathName);
|
||||||
DomainFolder localDestFolder =
|
DomainFolder localDestFolder =
|
||||||
(parentPath != null) ? ProjectDataUtils.lookupDomainPath(destinationFolder, parentPath)
|
(parentPath != null) ? ProjectDataUtils.lookupDomainPath(destinationFolder, parentPath)
|
||||||
: destinationFolder;
|
: destinationFolder;
|
||||||
if (localDestFolder != null) {
|
if (localDestFolder != null) {
|
||||||
if (localDestFolder.getFolder(filename) != null ||
|
if (isFolder && localDestFolder.getFolder(fileOrFolderName) != null ||
|
||||||
localDestFolder.getFile(filename) != null) {
|
!isFolder && localDestFolder.getFile(fileOrFolderName) != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFilenameTooLong() {
|
private boolean isNameTooLong() {
|
||||||
int maxNameLen = tool.getProject().getProjectData().getMaxNameLength();
|
int maxNameLen = tool.getProject().getProjectData().getMaxNameLength();
|
||||||
for (String pathPart : getName().split("/")) {
|
for (String pathPart : getName().split("/")) {
|
||||||
if (pathPart.length() >= maxNameLen) {
|
if (pathPart.length() >= maxNameLen) {
|
||||||
|
@ -535,17 +538,17 @@ public class ImporterDialog extends DialogComponentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getName() {
|
private String getName() {
|
||||||
return filenameTextField.getText().trim();
|
return nameTextField.getText().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setFilename(String s) {
|
private void setName(String s) {
|
||||||
if (userHasChangedName && validateFilename()) {
|
if (userHasChangedName && validateName()) {
|
||||||
// Changing the user's text is really annoying. Keep the user's filename, if it is valid
|
// Changing the user's text is really annoying. Keep the user's name, if it is valid
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
filenameTextField.setText(s);
|
nameTextField.setText(s);
|
||||||
filenameTextField.setCaretPosition(s.length());
|
nameTextField.setCaretPosition(s.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setSelectedLanguage(LanguageCompilerSpecPair lcsPair) {
|
protected void setSelectedLanguage(LanguageCompilerSpecPair lcsPair) {
|
||||||
|
@ -599,8 +602,8 @@ public class ImporterDialog extends DialogComponentProvider {
|
||||||
return languageTextField;
|
return languageTextField;
|
||||||
}
|
}
|
||||||
|
|
||||||
JTextField getFilenameTextField() {
|
JTextField getNameTextField() {
|
||||||
return filenameTextField;
|
return nameTextField;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.plugin.importer;
|
package ghidra.plugin.importer;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import ghidra.app.plugin.core.help.AboutDomainObjectUtils;
|
import ghidra.app.plugin.core.help.AboutDomainObjectUtils;
|
||||||
|
@ -311,8 +310,19 @@ public class ImporterUtilities {
|
||||||
doFSImportHelper((GFileSystemProgramProvider) refdFile.fsRef.getFilesystem(),
|
doFSImportHelper((GFileSystemProgramProvider) refdFile.fsRef.getFilesystem(),
|
||||||
gfile, destFolder, consumer, monitor);
|
gfile, destFolder, consumer, monitor);
|
||||||
if (program != null) {
|
if (program != null) {
|
||||||
doPostImportProcessing(tool, programManager, fsrl, Arrays.asList(program),
|
LoadResults<? extends DomainObject> loadResults = new LoadResults<>(program,
|
||||||
consumer, "", monitor);
|
program.getName(), destFolder.getPathname());
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
doPostImportProcessing(tool, programManager, fsrl, loadResults, consumer,
|
||||||
|
"", monitor);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (!success) {
|
||||||
|
program.release(consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
|
@ -331,16 +341,25 @@ public class ImporterUtilities {
|
||||||
Program program =
|
Program program =
|
||||||
pfs.getProgram(gfile, DefaultLanguageService.getLanguageService(), monitor, consumer);
|
pfs.getProgram(gfile, DefaultLanguageService.getLanguageService(), monitor, consumer);
|
||||||
|
|
||||||
if (program != null) {
|
if (program == null) {
|
||||||
String importFilename = ProjectDataUtils.getUniqueName(destFolder, program.getName());
|
return null;
|
||||||
if (importFilename == null) {
|
|
||||||
program.release(consumer);
|
|
||||||
throw new IOException("Unable to find unique name for " + program.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destFolder.createFile(importFilename, program, monitor);
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
String importFilename = ProjectDataUtils.getUniqueName(destFolder, program.getName());
|
||||||
|
if (importFilename == null) {
|
||||||
|
throw new IOException("Unable to find unique name for " + program.getName());
|
||||||
}
|
}
|
||||||
|
destFolder.createFile(importFilename, program, monitor);
|
||||||
|
success = true;
|
||||||
return program;
|
return program;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (!success) {
|
||||||
|
program.release(consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,13 +384,13 @@ public class ImporterUtilities {
|
||||||
|
|
||||||
Object consumer = new Object();
|
Object consumer = new Object();
|
||||||
MessageLog messageLog = new MessageLog();
|
MessageLog messageLog = new MessageLog();
|
||||||
List<DomainObject> importedObjects = loadSpec.getLoader().load(bp, programName,
|
LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||||
destFolder, loadSpec, options, messageLog, consumer, monitor);
|
.load(bp, programName, tool.getProject(), destFolder.getPathname(), loadSpec,
|
||||||
if (importedObjects == null) {
|
options, messageLog, consumer, monitor);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
doPostImportProcessing(tool, programManager, fsrl, importedObjects, consumer,
|
loadResults.save(tool.getProject(), consumer, messageLog, monitor);
|
||||||
|
|
||||||
|
doPostImportProcessing(tool, programManager, fsrl, loadResults, consumer,
|
||||||
messageLog.toString(), monitor);
|
messageLog.toString(), monitor);
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
|
@ -384,17 +403,16 @@ public class ImporterUtilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<DomainFile> doPostImportProcessing(PluginTool pluginTool,
|
private static Set<DomainFile> doPostImportProcessing(PluginTool pluginTool,
|
||||||
ProgramManager programManager, FSRL fsrl, List<DomainObject> importedObjects,
|
ProgramManager programManager, FSRL fsrl,
|
||||||
Object consumer, String importMessages, TaskMonitor monitor)
|
LoadResults<? extends DomainObject> loadResults, Object consumer, String importMessages,
|
||||||
throws CancelledException, IOException {
|
TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
boolean firstProgram = true;
|
boolean firstProgram = true;
|
||||||
Set<DomainFile> importedFilesSet = new HashSet<>();
|
Set<DomainFile> importedFilesSet = new HashSet<>();
|
||||||
for (DomainObject importedObject : importedObjects) {
|
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
|
||||||
if (importedObject instanceof Program) {
|
if (loaded.getDomainObject() instanceof Program program) {
|
||||||
Program program = (Program) importedObject;
|
|
||||||
ProgramMappingService.createAssociation(fsrl, program);
|
ProgramMappingService.createAssociation(fsrl, program);
|
||||||
|
|
||||||
if (programManager != null) {
|
if (programManager != null) {
|
||||||
|
@ -407,10 +425,10 @@ public class ImporterUtilities {
|
||||||
}
|
}
|
||||||
if (firstProgram) {
|
if (firstProgram) {
|
||||||
// currently we only show results for the imported program, not any libraries
|
// currently we only show results for the imported program, not any libraries
|
||||||
displayResults(pluginTool, importedObject, importedObject.getDomainFile(),
|
displayResults(pluginTool, loaded.getDomainObject(),
|
||||||
importMessages);
|
loaded.getDomainObject().getDomainFile(), importMessages);
|
||||||
}
|
}
|
||||||
importedObject.release(consumer);
|
loaded.release(consumer);
|
||||||
firstProgram = false;
|
firstProgram = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.plugins.importer.tasks;
|
package ghidra.plugins.importer.tasks;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
|
@ -26,8 +25,9 @@ import ghidra.app.services.ProgramManager;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.opinion.LoadSpec;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.formats.gfilesystem.*;
|
import ghidra.formats.gfilesystem.*;
|
||||||
|
import ghidra.framework.main.AppInfo;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.store.local.LocalFileSystem;
|
import ghidra.framework.store.local.LocalFileSystem;
|
||||||
import ghidra.plugin.importer.ProgramMappingService;
|
import ghidra.plugin.importer.ProgramMappingService;
|
||||||
|
@ -36,7 +36,8 @@ import ghidra.plugins.importer.batch.BatchGroup.BatchLoadConfig;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.exception.VersionException;
|
||||||
import ghidra.util.task.Task;
|
import ghidra.util.task.Task;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
@ -146,20 +147,21 @@ public class ImportBatchTask extends Task {
|
||||||
Object consumer = new Object();
|
Object consumer = new Object();
|
||||||
try {
|
try {
|
||||||
MessageLog messageLog = new MessageLog();
|
MessageLog messageLog = new MessageLog();
|
||||||
List<DomainObject> importedObjects = loadSpec.getLoader()
|
Project project = AppInfo.getActiveProject();
|
||||||
.load(byteProvider,
|
LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||||
fixupProjectFilename(destInfo.second), destInfo.first, loadSpec,
|
.load(byteProvider, fixupProjectFilename(destInfo.second), project,
|
||||||
|
destInfo.first.getPathname(), loadSpec,
|
||||||
getOptionsFor(batchLoadConfig, loadSpec, byteProvider), messageLog,
|
getOptionsFor(batchLoadConfig, loadSpec, byteProvider), messageLog,
|
||||||
consumer,
|
consumer, monitor);
|
||||||
monitor);
|
|
||||||
|
|
||||||
// TODO: accumulate batch results
|
// TODO: accumulate batch results
|
||||||
if (importedObjects != null) {
|
if (loadResults != null) {
|
||||||
try {
|
try {
|
||||||
processImportResults(importedObjects, batchLoadConfig, monitor);
|
loadResults.save(project, consumer, messageLog, monitor);
|
||||||
|
processImportResults(loadResults, batchLoadConfig, monitor);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
releaseAll(importedObjects, consumer);
|
loadResults.release(consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
totalAppsImported++;
|
totalAppsImported++;
|
||||||
|
@ -173,8 +175,7 @@ public class ImportBatchTask extends Task {
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
Msg.debug(this, "Batch Import cancelled");
|
Msg.debug(this, "Batch Import cancelled");
|
||||||
}
|
}
|
||||||
catch (DuplicateNameException | InvalidNameException | VersionException
|
catch (IOException | VersionException | IllegalArgumentException e) {
|
||||||
| IOException | IllegalArgumentException e) {
|
|
||||||
Msg.error(this, "Import failed for " + batchLoadConfig.getPreferredFileName(), e);
|
Msg.error(this, "Import failed for " + batchLoadConfig.getPreferredFileName(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,24 +191,15 @@ public class ImportBatchTask extends Task {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releaseAll(List<DomainObject> importedObjects, Object consumer) {
|
|
||||||
for (DomainObject obj : importedObjects) {
|
|
||||||
if (obj.isUsedBy(consumer)) {
|
|
||||||
obj.release(consumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* creates fsrl associations, updates task statistics, opens the imported program (if allowed)
|
* creates fsrl associations, updates task statistics, opens the imported program (if allowed)
|
||||||
*/
|
*/
|
||||||
private void processImportResults(List<DomainObject> importedObjects, BatchLoadConfig appInfo,
|
private void processImportResults(LoadResults<? extends DomainObject> loadResults,
|
||||||
TaskMonitor monitor) throws CancelledException, IOException {
|
BatchLoadConfig appInfo, TaskMonitor monitor) {
|
||||||
|
|
||||||
for (DomainObject obj : importedObjects) {
|
|
||||||
if (obj instanceof Program) {
|
|
||||||
Program program = (Program) obj;
|
|
||||||
|
|
||||||
|
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
||||||
|
DomainObject obj = loaded.getDomainObject();
|
||||||
|
if (obj instanceof Program program) {
|
||||||
if (programManager != null && totalObjsImported < MAX_PROGRAMS_TO_OPEN) {
|
if (programManager != null && totalObjsImported < MAX_PROGRAMS_TO_OPEN) {
|
||||||
programManager.openProgram(program,
|
programManager.openProgram(program,
|
||||||
totalObjsImported == 0 ? ProgramManager.OPEN_CURRENT
|
totalObjsImported == 0 ? ProgramManager.OPEN_CURRENT
|
||||||
|
|
|
@ -26,6 +26,8 @@ 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.AutoImporter;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.app.util.opinion.LoadException;
|
||||||
|
import ghidra.app.util.opinion.LoadResults;
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
|
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
@ -79,24 +81,34 @@ 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 {
|
||||||
program = AutoImporter.importByUsingBestGuess(provider, null, this, messageLog,
|
try {
|
||||||
TaskMonitor.DUMMY);
|
loadResults = AutoImporter.importByUsingBestGuess(provider, null, null, this,
|
||||||
|
|
||||||
if (program == null) {
|
|
||||||
program = AutoImporter.importAsBinary(provider, null, defaultLanguage, null, this,
|
|
||||||
messageLog, TaskMonitor.DUMMY);
|
messageLog, TaskMonitor.DUMMY);
|
||||||
|
program = loadResults.getPrimaryDomainObject();
|
||||||
}
|
}
|
||||||
if (program == null) {
|
catch (LoadException e) {
|
||||||
|
try {
|
||||||
|
program = AutoImporter
|
||||||
|
.importAsBinary(provider, null, null, defaultLanguage, null, this,
|
||||||
|
messageLog, TaskMonitor.DUMMY)
|
||||||
|
.getDomainObject();
|
||||||
|
}
|
||||||
|
catch (LoadException e1) {
|
||||||
throw new GhidraException(
|
throw new GhidraException(
|
||||||
"Can't create program from input: " + messageLog.toString());
|
"Can't create program from input: " + messageLog.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
messageLog.appendException(e);
|
messageLog.appendException(e);
|
||||||
throw new GhidraException(e);
|
throw new GhidraException(e);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
if (loadResults != null) {
|
||||||
|
loadResults.releaseNonPrimary(this);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
provider.close();
|
provider.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,12 @@ package ghidra.program.util;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.opinion.ElfLoader;
|
import ghidra.app.util.opinion.ElfLoader;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.app.util.opinion.Loaded;
|
||||||
|
import ghidra.framework.model.DomainObject;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
|
@ -41,23 +43,24 @@ public class ELFExternalSymbolResolver {
|
||||||
* already existing / imported libraries.
|
* already existing / imported libraries.
|
||||||
* <p>
|
* <p>
|
||||||
*
|
*
|
||||||
* @param program ELF {@link Program} to fix.
|
* @param loadedPrograms ELF {@link Loaded} {@link Program}s to fix..
|
||||||
* @param saveIfModified boolean flag, if true the program will be saved if there was a
|
|
||||||
* modification.
|
|
||||||
* @param messageLog {@link MessageLog} to write info message to.
|
* @param messageLog {@link MessageLog} to write info message to.
|
||||||
* @param monitor {@link TaskMonitor} to watch for cancel and update with progress.
|
* @param monitor {@link TaskMonitor} to watch for cancel and update with progress.
|
||||||
* @throws CancelledException if user cancels
|
* @throws CancelledException if user cancels
|
||||||
* @throws IOException if error reading
|
* @throws IOException if error reading
|
||||||
*/
|
*/
|
||||||
public static void fixUnresolvedExternalSymbols(Program program, boolean saveIfModified,
|
public static void fixUnresolvedExternalSymbols(List<Loaded<Program>> loadedPrograms,
|
||||||
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
|
MessageLog messageLog, TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
DomainFolder domainFolder = program.getDomainFile().getParent();
|
Map<String, Loaded<Program>> loadedByName = loadedPrograms.stream()
|
||||||
if (domainFolder == null) {
|
.collect(
|
||||||
return; // headless case with nothing pre-saved...currently unsupported
|
Collectors.toMap(loaded -> loaded.getName(), loaded -> loaded));
|
||||||
}
|
|
||||||
ProjectData projectData = domainFolder.getProjectData();
|
|
||||||
|
|
||||||
Collection<Long> unresolvedExternalFunctionIds = getUnresolvedExternalFunctionIds(program);
|
monitor.initialize(loadedByName.size());
|
||||||
|
for (Loaded<Program> loadedProgram : loadedByName.values()) {
|
||||||
|
Program program = loadedProgram.getDomainObject();
|
||||||
|
|
||||||
|
Collection<Long> unresolvedExternalFunctionIds =
|
||||||
|
getUnresolvedExternalFunctionIds(program);
|
||||||
if (unresolvedExternalFunctionIds.size() == 0) {
|
if (unresolvedExternalFunctionIds.size() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -81,50 +84,23 @@ public class ELFExternalSymbolResolver {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
DomainFile libDomainFile = projectData.getFile(libPath);
|
Loaded<Program> loadedLib = loadedByName.get(libName);
|
||||||
if (libDomainFile == null) {
|
if (loadedLib == null) {
|
||||||
messageLog.appendMsg("Referenced external program not found: " + libPath);
|
messageLog.appendMsg("Referenced external program not found: " + libName);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object consumer = new Object();
|
DomainObject libDomainObject = loadedLib.getDomainObject();
|
||||||
DomainObject libDomainObject = null;
|
|
||||||
try {
|
|
||||||
libDomainObject =
|
|
||||||
libDomainFile.getDomainObject(consumer, false, false, monitor);
|
|
||||||
if (!(libDomainObject instanceof Program)) {
|
|
||||||
messageLog.appendMsg(
|
|
||||||
"Referenced external program is not a program: " + libPath);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
monitor.setMessage("Resolving symbols published by library " + libName);
|
monitor.setMessage("Resolving symbols published by library " + libName);
|
||||||
resolveSymbolsToLibrary(program, unresolvedExternalFunctionIds, extLibrary,
|
resolveSymbolsToLibrary(program, unresolvedExternalFunctionIds, extLibrary,
|
||||||
(Program) libDomainObject, messageLog, monitor);
|
(Program) libDomainObject, messageLog, monitor);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
|
||||||
// failed to open library
|
|
||||||
messageLog.appendMsg("Failed to open library dependency project file: " +
|
|
||||||
libDomainFile.getPathname());
|
|
||||||
}
|
|
||||||
catch (VersionException e) {
|
|
||||||
messageLog.appendMsg(
|
|
||||||
"Referenced external program requires updgrade, unable to consider symbols: " +
|
|
||||||
libPath);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (libDomainObject != null) {
|
|
||||||
libDomainObject.release(consumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
messageLog.appendMsg("Unresolved external symbols which remain: " +
|
messageLog.appendMsg("Unresolved external symbols which remain: " +
|
||||||
unresolvedExternalFunctionIds.size());
|
unresolvedExternalFunctionIds.size());
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(transactionID, true);
|
program.endTransaction(transactionID, true);
|
||||||
}
|
}
|
||||||
if (saveIfModified && program.canSave() && program.isChanged()) {
|
|
||||||
program.save("ExternalSymbolResolver", monitor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +199,7 @@ public class ELFExternalSymbolResolver {
|
||||||
return orderLibraryMap.values();
|
return orderLibraryMap.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Library> getLibrarySearchList(Program program) {
|
public static List<Library> getLibrarySearchList(Program program) {
|
||||||
List<Library> result = new ArrayList<>();
|
List<Library> result = new ArrayList<>();
|
||||||
ExternalManager externalManager = program.getExternalManager();
|
ExternalManager externalManager = program.getExternalManager();
|
||||||
for (String libName : getOrderedLibraryNamesNeeded(program)) {
|
for (String libName : getOrderedLibraryNamesNeeded(program)) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ import ghidra.file.formats.android.xml.AndroidXmlFileSystem;
|
||||||
import ghidra.file.formats.zip.ZipFileSystem;
|
import ghidra.file.formats.zip.ZipFileSystem;
|
||||||
import ghidra.formats.gfilesystem.FileSystemService;
|
import ghidra.formats.gfilesystem.FileSystemService;
|
||||||
import ghidra.formats.gfilesystem.GFile;
|
import ghidra.formats.gfilesystem.GFile;
|
||||||
import ghidra.framework.model.DomainFolder;
|
import ghidra.framework.model.Project;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
@ -64,12 +64,13 @@ public class ApkLoader extends DexLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<LoadedProgram> loadProgram(ByteProvider provider, String programName,
|
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||||
Object consumer, TaskMonitor monitor) throws CancelledException, IOException {
|
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||||
|
throws IOException, LoadException, CancelledException {
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
List<LoadedProgram> allLoadedPrograms = new ArrayList<>();
|
List<Loaded<Program>> allLoadedPrograms = new ArrayList<>();
|
||||||
int dexIndex = 1;//DEX file numbering starts at 1
|
int dexIndex = 1;//DEX file numbering starts at 1
|
||||||
try (ZipFileSystem zipFS = openAPK(provider, monitor)) {
|
try (ZipFileSystem zipFS = openAPK(provider, monitor)) {
|
||||||
while (!monitor.isCancelled()) {
|
while (!monitor.isCancelled()) {
|
||||||
|
@ -86,9 +87,10 @@ public class ApkLoader extends DexLoader {
|
||||||
try (ByteProvider dexProvider =
|
try (ByteProvider dexProvider =
|
||||||
zipFS.getByteProvider(classesDexFile, monitor)) {
|
zipFS.getByteProvider(classesDexFile, monitor)) {
|
||||||
// defer to the super class (DexLoader) to actually load the DEX file
|
// defer to the super class (DexLoader) to actually load the DEX file
|
||||||
List<LoadedProgram> loadedPrograms =
|
List<Loaded<Program>> loadedPrograms =
|
||||||
super.loadProgram(dexProvider, classesDexFile.getName(), programFolder,
|
super.loadProgram(dexProvider, classesDexFile.getName(), project,
|
||||||
loadSpec, options, log, consumer, monitor);
|
concatenatePaths(programFolderPath, programName), loadSpec, options,
|
||||||
|
log, consumer, monitor);
|
||||||
|
|
||||||
allLoadedPrograms.addAll(loadedPrograms);
|
allLoadedPrograms.addAll(loadedPrograms);
|
||||||
}
|
}
|
||||||
|
@ -104,14 +106,16 @@ public class ApkLoader extends DexLoader {
|
||||||
release(allLoadedPrograms, consumer);
|
release(allLoadedPrograms, consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
link(allLoadedPrograms.stream().map(e -> e.program()).toList(), log, monitor);
|
if (allLoadedPrograms.isEmpty()) {
|
||||||
|
throw new LoadException("Operation finished with no programs to load");
|
||||||
|
}
|
||||||
|
link(allLoadedPrograms.stream().map(e -> e.getDomainObject()).toList(), log, monitor);
|
||||||
return allLoadedPrograms;
|
return allLoadedPrograms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isOverrideMainProgramName() {
|
public boolean loadsIntoNewFolder() {
|
||||||
//preserve the classesX.dex file names...
|
return true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -198,7 +198,7 @@ public class MachoPrelinkFileSystem extends GFileSystemBase implements GFileSyst
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(id, success);
|
program.endTransaction(id, true); // More efficient to commit when program will be discarded
|
||||||
if (!success) {
|
if (!success) {
|
||||||
program.release(consumer);
|
program.release(consumer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
//@category FunctionID
|
//@category FunctionID
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
|
@ -26,9 +27,9 @@ 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.*;
|
||||||
import ghidra.app.util.opinion.Loader;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.app.util.opinion.MSCoffLoader;
|
|
||||||
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;
|
||||||
|
@ -95,40 +96,43 @@ public class ImportMSLibs extends GhidraScript {
|
||||||
}
|
}
|
||||||
offsetsSeen.add(archiveMemberHeader.getPayloadOffset());
|
offsetsSeen.add(archiveMemberHeader.getPayloadOffset());
|
||||||
if (archiveMemberHeader.isCOFF()) {
|
if (archiveMemberHeader.isCOFF()) {
|
||||||
|
String preferredName = archiveMemberHeader.getName();
|
||||||
try (ByteProvider coffProvider = new ByteProviderWrapper(provider,
|
try (ByteProvider coffProvider = new ByteProviderWrapper(provider,
|
||||||
archiveMemberHeader.getPayloadOffset(), archiveMemberHeader.getSize())) {
|
archiveMemberHeader.getPayloadOffset(), archiveMemberHeader.getSize())) {
|
||||||
CoffFileHeader header = new CoffFileHeader(coffProvider);
|
CoffFileHeader header = new CoffFileHeader(coffProvider);
|
||||||
if (CoffMachineType.isMachineTypeDefined(header.getMagic())) {
|
if (CoffMachineType.isMachineTypeDefined(header.getMagic())) {
|
||||||
String preferredName = archiveMemberHeader.getName();
|
|
||||||
String[] splits = splitPreferredName(preferredName);
|
String[] splits = splitPreferredName(preferredName);
|
||||||
|
|
||||||
List<Program> programs =
|
LoadResults<? extends DomainObject> loadResults =
|
||||||
AutoImporter.importFresh(
|
AutoImporter.importFresh(
|
||||||
coffProvider,
|
coffProvider,
|
||||||
root,
|
state.getProject(),
|
||||||
|
root.getPathname(),
|
||||||
this,
|
this,
|
||||||
log,
|
log,
|
||||||
new CancelOnlyWrappingTaskMonitor(monitor),
|
new CancelOnlyWrappingTaskMonitor(monitor),
|
||||||
LOADER_FILTER,
|
LOADER_FILTER,
|
||||||
LOADSPEC_CHOOSER,
|
LOADSPEC_CHOOSER,
|
||||||
mangleNameBecauseDomainFoldersAreSoRetro(splits[splits.length - 1]),
|
mangleNameBecauseDomainFoldersAreSoRetro(splits[splits.length - 1]),
|
||||||
OptionChooser.DEFAULT_OPTIONS,
|
OptionChooser.DEFAULT_OPTIONS);
|
||||||
MultipleProgramsStrategy.ONE_PROGRAM_OR_EXCEPTION);
|
|
||||||
|
|
||||||
if (programs == null || programs.isEmpty()) {
|
try {
|
||||||
printerr("no programs loaded from " + file + " - " +
|
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
||||||
preferredName);
|
if (loaded.getDomainObject() instanceof Program program) {
|
||||||
}
|
loaded.save(state.getProject(), log, monitor);
|
||||||
|
|
||||||
if (programs != null) {
|
|
||||||
for (Program program : programs) {
|
|
||||||
program.release(this);
|
|
||||||
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 {
|
||||||
|
loadResults.release(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (LoadException e) {
|
||||||
|
printerr("no programs loaded from " + file + " - " + preferredName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,10 +64,8 @@ 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.*;
|
||||||
import ghidra.app.util.opinion.Loader;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.app.util.opinion.MSCoffLoader;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.model.DomainFile;
|
|
||||||
import ghidra.framework.model.DomainFolder;
|
|
||||||
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;
|
||||||
|
@ -198,24 +196,30 @@ 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 = AutoImporter.importFresh(coffProvider,
|
||||||
|
state.getProject(), pair.first.getPathname(), this, log,
|
||||||
|
monitor, LOADER_FILTER, LOADSPEC_CHOOSER, pair.second,
|
||||||
|
OptionChooser.DEFAULT_OPTIONS);
|
||||||
|
|
||||||
List<Program> programs = AutoImporter.importFresh(coffProvider,
|
for (Loaded<? extends DomainObject> loaded : loadResults) {
|
||||||
pair.first, this, log, monitor, LOADER_FILTER, LOADSPEC_CHOOSER,
|
if (loaded.getDomainObject() instanceof Program program) {
|
||||||
pair.second, OptionChooser.DEFAULT_OPTIONS,
|
loaded.save(state.getProject(), log, monitor);
|
||||||
MultipleProgramsStrategy.ONE_PROGRAM_OR_EXCEPTION);
|
println(
|
||||||
|
"Imported " + program.getDomainFile().getPathname());
|
||||||
if (programs != null) {
|
|
||||||
for (Program program : programs) {
|
|
||||||
println("Imported " + program.getDomainFile().getPathname());
|
|
||||||
DomainFile progFile = program.getDomainFile();
|
DomainFile progFile = program.getDomainFile();
|
||||||
|
|
||||||
program.release(this);
|
|
||||||
|
|
||||||
if (!progFile.isVersioned()) {
|
if (!progFile.isVersioned()) {
|
||||||
progFile.addToVersionControl(initalCheckInComment, false,
|
progFile.addToVersionControl(initalCheckInComment,
|
||||||
monitor);
|
false, monitor);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (loadResults != null) {
|
||||||
|
loadResults.release(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
*/
|
*/
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import generic.stl.Pair;
|
import generic.stl.Pair;
|
||||||
|
@ -25,11 +26,10 @@ 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.*;
|
||||||
import ghidra.app.util.opinion.Loader;
|
import ghidra.app.util.opinion.*;
|
||||||
import ghidra.app.util.opinion.MSCoffLoader;
|
|
||||||
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.listing.Program;
|
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
@ -152,16 +152,18 @@ public class RecursiveRecursiveMSLibImport extends GhidraScript {
|
||||||
Pair<DomainFolder, String> pair =
|
Pair<DomainFolder, String> pair =
|
||||||
establishProgramFolder(currentLibrary, preferredName);
|
establishProgramFolder(currentLibrary, preferredName);
|
||||||
|
|
||||||
List<Program> programs = AutoImporter.importFresh(coffProvider,
|
LoadResults<? extends DomainObject> loadResults =
|
||||||
pair.first, this, log, monitor, LOADER_FILTER, LOADSPEC_CHOOSER,
|
AutoImporter.importFresh(coffProvider, state.getProject(),
|
||||||
|
pair.first.getPathname(), this, log, monitor, LOADER_FILTER,
|
||||||
|
LOADSPEC_CHOOSER,
|
||||||
mangleNameBecauseDomainFoldersAreSoRetro(pair.second),
|
mangleNameBecauseDomainFoldersAreSoRetro(pair.second),
|
||||||
OptionChooser.DEFAULT_OPTIONS,
|
OptionChooser.DEFAULT_OPTIONS);
|
||||||
MultipleProgramsStrategy.ONE_PROGRAM_OR_EXCEPTION);
|
|
||||||
|
|
||||||
if (programs != null) {
|
try {
|
||||||
for (Program program : programs) {
|
loadResults.save(state.getProject(), this, log, monitor);
|
||||||
program.release(this);
|
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
loadResults.release(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
@ -30,8 +29,10 @@ 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.*;
|
||||||
|
import ghidra.app.util.opinion.LoadResults;
|
||||||
import ghidra.app.util.opinion.LoaderService;
|
import ghidra.app.util.opinion.LoaderService;
|
||||||
import ghidra.framework.main.DataTreeDialog;
|
import ghidra.framework.main.DataTreeDialog;
|
||||||
|
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;
|
||||||
|
@ -290,16 +291,18 @@ public class ReferencesPluginScreenShots extends GhidraScreenShotGenerator {
|
||||||
private void importFile(File file) throws CancelledException, DuplicateNameException,
|
private void importFile(File file) throws CancelledException, DuplicateNameException,
|
||||||
InvalidNameException, VersionException, IOException {
|
InvalidNameException, VersionException, IOException {
|
||||||
String programNameOverride = null;
|
String programNameOverride = null;
|
||||||
List<Program> programs = AutoImporter.importFresh(file, null, this, new MessageLog(),
|
Project project = env.getProject();
|
||||||
|
LoadResults<Program> loadResults = AutoImporter.importFresh(file, project,
|
||||||
|
project.getProjectData().getRootFolder().getPathname(), this, new MessageLog(),
|
||||||
TaskMonitor.DUMMY, LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED,
|
TaskMonitor.DUMMY, LoaderService.ACCEPT_ALL, LoadSpecChooser.CHOOSE_THE_FIRST_PREFERRED,
|
||||||
programNameOverride, OptionChooser.DEFAULT_OPTIONS,
|
programNameOverride, OptionChooser.DEFAULT_OPTIONS);
|
||||||
MultipleProgramsStrategy.ALL_PROGRAMS);
|
|
||||||
Program p = programs.get(0);
|
try {
|
||||||
env.getProject()
|
loadResults.getPrimary().save(project, new MessageLog(), TaskMonitor.DUMMY);
|
||||||
.getProjectData()
|
}
|
||||||
.getRootFolder()
|
finally {
|
||||||
.createFile(p.getName(), p,
|
loadResults.release(this);
|
||||||
TaskMonitor.DUMMY);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue