GP-5343: Importer filesystem mirroring
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.1 KiB |
|
@ -280,6 +280,12 @@
|
|||
will be used to create a destination folder in the current project under the root folder
|
||||
specified by the <B>Destination Folder</B> field.</LI><BR>
|
||||
|
||||
<LI><B>Mirror Filesystem</B> - If checked, the filesystem path layout of any imported
|
||||
binaries will be mirrored in the destination folder. Any filesystem directory and file
|
||||
soft links will be mirrored as
|
||||
<A href="help/topics/FrontEndPlugin/Ghidra_Front_end.htm#Create_File_Links">Ghidra
|
||||
project folder and file links.</A>.</LI><BR>
|
||||
|
||||
<LI><B>Options...</B> - This button will pop up format specific options for the
|
||||
import.</LI><BR>
|
||||
|
||||
|
@ -335,7 +341,9 @@
|
|||
|
||||
<BLOCKQUOTE>
|
||||
<P>The project folder that will get searched for existing library programs. If left
|
||||
empty, the folder that the main program is being imported to will be searched.</P>
|
||||
empty, the folder that the main program is being imported to will be searched.
|
||||
<B><I>This option is hidden and set to the program destination folder if filesystem
|
||||
mirroring is enabled in the Importer Dialog</I></B>.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4>Load Libraries From Disk</H4>
|
||||
|
@ -360,7 +368,20 @@
|
|||
|
||||
<BLOCKQUOTE>
|
||||
<P>The project folder where newly loaded library programs will get created. If left
|
||||
empty, they will get created in the same folder as the main program being imported.</P>
|
||||
empty, they will get created in the same folder as the main program being imported.
|
||||
<B><I>This option is hidden and set to the program destination folder if filesystem
|
||||
mirroring is enabled in the Importer Dialog</I></B>.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4>Mirror Library Disk Layout</H4>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>If selected, the filesystem path layout of all imported libraries are
|
||||
mirrored in the library destination folder. Any filesystem directory and file
|
||||
soft links will be mirrored as
|
||||
<A href="help/topics/FrontEndPlugin/Ghidra_Front_end.htm#Create_File_Links">Ghidra
|
||||
project folder and file links</A>. <B><I>This option is hidden and enabled if filesystem
|
||||
mirroring is enabled in the Importer Dialog</I></B>.</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
@ -718,6 +739,9 @@
|
|||
added via the
|
||||
<A href="help/topics/FileSystemBrowserPlugin/FileSystemBrowserPlugin.html#FSB_Add_Library_Search_Path">File System Browser context menu</A>.
|
||||
</P>
|
||||
<P>If importing with filesystem mirroring activated, these paths also are used to lookup
|
||||
already-imported libraries that are rooted in the project at the specified destination folder.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P align="center"><IMG alt="" src="images/SearchPathsDialog.png"></P>
|
||||
|
@ -937,6 +961,16 @@
|
|||
to the path the file was in its archive.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4>Mirror Filesystem</H4>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>If selected, the filesystem path layout of any imported
|
||||
binaries will be mirrored in the destination folder. Any filesystem directory and file
|
||||
soft links will be mirrored as
|
||||
<A href="help/topics/FrontEndPlugin/Ghidra_Front_end.htm#Create_File_Links">Ghidra
|
||||
project folder and file links.</A></P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4>Project Destination</H4>
|
||||
<BLOCKQUOTE>
|
||||
<P>This shows the destination folder in the project that will be the root folder for storing
|
||||
|
|
|
@ -24,6 +24,7 @@ import ghidra.app.util.bin.ByteProvider;
|
|||
import ghidra.app.util.bin.format.dwarf.external.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.app.util.opinion.Loader.ImporterSettings;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.plugin.importer.ImporterUtilities;
|
||||
|
@ -84,11 +85,13 @@ public class ExternalDebugFileSectionProvider extends BaseSectionProvider {
|
|||
|
||||
Loader origLoader = origLoadSpec.getLoader();
|
||||
List<Option> defaultOptions = origLoader.getDefaultOptions(debugFileByteProvider,
|
||||
origLoadSpec, debugProgram, false);
|
||||
origLoadSpec, debugProgram, false, false);
|
||||
|
||||
ElfLoader elfLoader = new ElfLoader();
|
||||
elfLoader.load(debugFileByteProvider, null, defaultOptions, debugProgram, monitor,
|
||||
new MessageLog());
|
||||
ImporterSettings settings =
|
||||
new ImporterSettings(debugFileByteProvider, debugProgram.getName(), null, null,
|
||||
false, origLoadSpec, defaultOptions, consumer, new MessageLog(), monitor);
|
||||
elfLoader.load(debugProgram, settings);
|
||||
|
||||
ExternalDebugFileSectionProvider result = new ExternalDebugFileSectionProvider(
|
||||
debugProgram, debugFileByteProvider.getFSRL());
|
||||
|
|
|
@ -52,6 +52,7 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
SCRIPT_LOG("-scriptlog", true, "<path to script log file>"),
|
||||
LOG("-log", true, "<path to log file>"),
|
||||
OVERWRITE("-overwrite", false),
|
||||
MIRROR("-mirror", false),
|
||||
RECURSIVE("-recursive", false),
|
||||
READ_ONLY("-readOnly", false),
|
||||
DELETE_PROJECT("-deleteProject", false),
|
||||
|
@ -243,6 +244,9 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
else if (checkArgument(Arg.OVERWRITE, args, argi)) {
|
||||
options.enableOverwriteOnConflict(true);
|
||||
}
|
||||
else if (checkArgument(Arg.MIRROR, args, argi)) {
|
||||
options.enableMirroring(true);
|
||||
}
|
||||
else if (checkArgument(Arg.NO_ANALYSIS, args, argi)) {
|
||||
options.enableAnalysis(false);
|
||||
}
|
||||
|
|
|
@ -1543,6 +1543,7 @@ public class HeadlessAnalyzer {
|
|||
.source(fsrl)
|
||||
.project(project)
|
||||
.projectFolderPath(folderPath)
|
||||
.mirror(options.mirror)
|
||||
.language(options.language)
|
||||
.compiler(options.compilerSpec)
|
||||
.loaders(options.loaderClass)
|
||||
|
|
|
@ -61,6 +61,9 @@ public class HeadlessOptions {
|
|||
// -overwrite
|
||||
boolean overwrite;
|
||||
|
||||
// -mirror
|
||||
boolean mirror;
|
||||
|
||||
// -recursive
|
||||
boolean recursive;
|
||||
Integer recursiveDepth; // 'null' means use default depth, which is different for files vs dirs
|
||||
|
@ -320,6 +323,15 @@ public class HeadlessOptions {
|
|||
this.overwrite = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disables mirroring of the imported file(s) filesystem path in the project.
|
||||
*
|
||||
* @param enabled True if filesystem mirroring should happen; otherwise, false.
|
||||
*/
|
||||
public void enableMirroring(boolean enabled) {
|
||||
this.mirror = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used to enable recursive processing of files during
|
||||
* <code>-import</code> or <code>-process</code> modes. In order for recursive processing of
|
||||
|
|
|
@ -40,9 +40,10 @@ public class DomainFolderOption extends Option {
|
|||
*
|
||||
* @param name The name of the option
|
||||
* @param arg The option's command line argument (could be null)
|
||||
* @param hidden true if this option should be hidden from the user; otherwise, false
|
||||
*/
|
||||
public DomainFolderOption(String name, String arg) {
|
||||
super(name, String.class, "", arg, null, Loader.OPTIONS_PROJECT_SAVE_STATE_KEY, false);
|
||||
public DomainFolderOption(String name, String arg, boolean hidden) {
|
||||
super(name, String.class, "", arg, null, Loader.OPTIONS_PROJECT_SAVE_STATE_KEY, hidden);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,6 +86,6 @@ public class DomainFolderOption extends Option {
|
|||
|
||||
@Override
|
||||
public Option copy() {
|
||||
return new DomainFolderOption(getName(), getArg());
|
||||
return new DomainFolderOption(getName(), getArg(), isHidden());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,14 +85,9 @@ public class LibrarySearchPathManager {
|
|||
}
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
try {
|
||||
File f = new File(path);
|
||||
if (f.exists() && f.isAbsolute()) {
|
||||
fsrl = fsService.getLocalFSRL(f.getCanonicalFile());
|
||||
}
|
||||
}
|
||||
catch (IOException e2) {
|
||||
log.appendException(e2);
|
||||
fsrl = fsService.getLocalFSRL(f);
|
||||
}
|
||||
}
|
||||
if (fsrl != null) {
|
||||
|
|
|
@ -24,6 +24,7 @@ import generic.stl.Pair;
|
|||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.app.util.opinion.Loader.ImporterSettings;
|
||||
import ghidra.formats.gfilesystem.FSRL;
|
||||
import ghidra.formats.gfilesystem.FileSystemService;
|
||||
import ghidra.framework.model.*;
|
||||
|
@ -62,6 +63,7 @@ public class ProgramLoader {
|
|||
private byte[] bytes;
|
||||
private Project project;
|
||||
private String projectFolderPath;
|
||||
private boolean mirror;
|
||||
private String importNameOverride;
|
||||
private Predicate<Loader> loaderFilter = LoaderService.ACCEPT_ALL;
|
||||
private List<Pair<String, String>> loaderArgs = new ArrayList<>();
|
||||
|
@ -175,15 +177,12 @@ public class ProgramLoader {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the suggested project folder path for the {@link Loaded} {@link Program}s. This is
|
||||
* just a suggestion, and a {@link Loader} implementation reserves the right to change it
|
||||
* for each {@link Loaded} result. The {@link Loaded} results should be queried for their
|
||||
* true project folder paths using {@link Loaded#getProjectFolderPath()}.
|
||||
* Sets the project folder path to load into.
|
||||
* <p>
|
||||
* The default project folder path is the root of the project ({@code "/"}).
|
||||
*
|
||||
* @param path The suggested project folder path. A {@code null} value will revert the path
|
||||
* back to the default value of ({@code "/"}).
|
||||
* @param path The project folder path. A {@code null} value will revert the path back to
|
||||
* the default value of ({@code "/"}).
|
||||
* @return This {@link Builder}
|
||||
*/
|
||||
public Builder projectFolderPath(String path) {
|
||||
|
@ -191,6 +190,21 @@ public class ProgramLoader {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not the absolute filesystem path of each {@link Loaded} {@link Program}
|
||||
* should be mirrored in the project, rooted at the specified
|
||||
* {@link #projectFolderPath(String) project folder path}.
|
||||
* <p>
|
||||
* By default, mirroring is off.
|
||||
*
|
||||
* @param shouldMirror True if filesystem mirroring should happen; otherwise, false.
|
||||
* @return This {@link Builder}
|
||||
*/
|
||||
public Builder mirror(boolean shouldMirror) {
|
||||
this.mirror = shouldMirror;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name to use for the imported {@link Program}.
|
||||
* <p>
|
||||
|
@ -454,8 +468,21 @@ public class ProgramLoader {
|
|||
|
||||
LoadSpec loadSpec = getLoadSpec(p);
|
||||
List<Option> loaderOptions = getLoaderOptions(p, loadSpec);
|
||||
String importName = importNameOverride != null ? importNameOverride
|
||||
String importName;
|
||||
if (mirror) {
|
||||
if (importNameOverride != null) {
|
||||
throw new LoadException("Cannot override import name if mirroring");
|
||||
}
|
||||
FSRL f = p.getFSRL();
|
||||
if (f == null) {
|
||||
throw new LoadException("Mirroring requires a file-based source");
|
||||
}
|
||||
importName = f.getPath();
|
||||
}
|
||||
else {
|
||||
importName = importNameOverride != null ? importNameOverride
|
||||
: loadSpec.getLoader().getPreferredFileName(p);
|
||||
}
|
||||
|
||||
// Load
|
||||
Msg.info(ProgramLoader.class, "Using Loader: " + loadSpec.getLoader().getName());
|
||||
|
@ -463,9 +490,11 @@ public class ProgramLoader {
|
|||
"Using Language/Compiler: " + loadSpec.getLanguageCompilerSpec());
|
||||
Msg.info(ProgramLoader.class, "Using Library Search Path: " +
|
||||
Arrays.toString(LibrarySearchPathManager.getLibraryPaths()));
|
||||
LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||
.load(p, importName, project, projectFolderPath, loadSpec, loaderOptions,
|
||||
log, Objects.requireNonNullElse(consumer, this), monitor);
|
||||
ImporterSettings settings = new ImporterSettings(p, importName, project,
|
||||
projectFolderPath, mirror, loadSpec, loaderOptions,
|
||||
Objects.requireNonNullElse(consumer, this), log, monitor);
|
||||
LoadResults<? extends DomainObject> loadResults =
|
||||
loadSpec.getLoader().load(settings);
|
||||
|
||||
// Optionally echo loader message log to application.log
|
||||
if (!Loader.loggingDisabled && log.hasMessages()) {
|
||||
|
@ -568,7 +597,8 @@ public class ProgramLoader {
|
|||
*/
|
||||
private List<Option> getLoaderOptions(ByteProvider p, LoadSpec loadSpec)
|
||||
throws LanguageNotFoundException, LoadException {
|
||||
List<Option> options = loadSpec.getLoader().getDefaultOptions(p, loadSpec, null, false);
|
||||
List<Option> options =
|
||||
loadSpec.getLoader().getDefaultOptions(p, loadSpec, null, false, mirror);
|
||||
if (options == null) {
|
||||
throw new LoadException("Cannot load with null options");
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import ghidra.app.util.bin.ByteProvider;
|
|||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -46,9 +45,9 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
List<Option> list =
|
||||
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
List<Option> list = super.getDefaultOptions(provider, loadSpec, domainObject,
|
||||
loadIntoProgram, mirrorFsLayout);
|
||||
list.add(new Option(ORDINAL_LOOKUP_OPTION_NAME, ORDINAL_LOOKUP_OPTION_DEFAULT,
|
||||
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-ordinalLookup"));
|
||||
return list;
|
||||
|
@ -71,26 +70,27 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldSearchAllPaths(Program program, List<Option> options, MessageLog log) {
|
||||
return shouldPerformOrdinalLookup(options);
|
||||
protected boolean shouldSearchAllPaths(Program program, ImporterSettings settings) {
|
||||
return shouldPerformOrdinalLookup(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processLibrary(Program lib, String libName, FSRL libFsrl, ByteProvider provider,
|
||||
Queue<UnprocessedLibrary> unprocessed, int depth, LoadSpec loadSpec,
|
||||
List<Option> options, MessageLog log, TaskMonitor monitor)
|
||||
protected void processLibrary(Program lib, String libName, FSRL libFsrl,
|
||||
Queue<UnprocessedLibrary> unprocessed, int depth, ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
int size = loadSpec.getLanguageCompilerSpec().getLanguageDescription().getSize();
|
||||
MessageLog log = settings.log();
|
||||
int size = settings.loadSpec().getLanguageCompilerSpec().getLanguageDescription().getSize();
|
||||
ResourceFile existingExportsFile = LibraryLookupTable.getExistingExportsFile(libName, size);
|
||||
|
||||
if (!shouldPerformOrdinalLookup(options)) {
|
||||
if (!shouldPerformOrdinalLookup(settings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create exports file if necessary
|
||||
if (existingExportsFile == null) {
|
||||
try {
|
||||
ResourceFile newExportsFile = LibraryLookupTable.createFile(lib, true, monitor);
|
||||
ResourceFile newExportsFile =
|
||||
LibraryLookupTable.createFile(lib, true, settings.monitor());
|
||||
log.appendMsg("Created exports file: " + newExportsFile);
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
@ -126,23 +126,22 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms,
|
||||
ImporterSettings settings) throws CancelledException, IOException {
|
||||
|
||||
if (shouldPerformOrdinalLookup(options)) {
|
||||
if (shouldPerformOrdinalLookup(settings)) {
|
||||
List<Loaded<Program>> saveablePrograms = loadedPrograms
|
||||
.stream()
|
||||
.filter(loaded -> loaded.check(Predicate.not(Program::isTemporary)))
|
||||
.toList();
|
||||
monitor.initialize(saveablePrograms.size());
|
||||
settings.monitor().initialize(saveablePrograms.size());
|
||||
for (Loaded<Program> loadedProgram : saveablePrograms) {
|
||||
monitor.checkCancelled();
|
||||
settings.monitor().checkCancelled();
|
||||
Program program = loadedProgram.getDomainObject(this);
|
||||
int id = program.startTransaction("Ordinal fixups");
|
||||
try {
|
||||
applyLibrarySymbols(program, messageLog, monitor);
|
||||
applyImports(program, messageLog, monitor);
|
||||
applyLibrarySymbols(program, settings.log(), settings.monitor());
|
||||
applyImports(program, settings.log(), settings.monitor());
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(id, true); // More efficient to commit when program will be discarded
|
||||
|
@ -151,8 +150,7 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
|||
}
|
||||
}
|
||||
|
||||
super.postLoadProgramFixups(loadedPrograms, project, loadSpec, options, messageLog,
|
||||
monitor);
|
||||
super.postLoadProgramFixups(loadedPrograms, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -164,11 +162,11 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
|||
/**
|
||||
* Checks to see if ordinal lookup should be performed
|
||||
*
|
||||
* @param options a {@link List} of {@link Option}s
|
||||
* @param settings The {@link Loader.ImporterSettings}
|
||||
* @return True if ordinal lookup should be performed; otherwise, false
|
||||
*/
|
||||
private boolean shouldPerformOrdinalLookup(List<Option> options) {
|
||||
return OptionUtils.getOption(ORDINAL_LOOKUP_OPTION_NAME, options,
|
||||
private boolean shouldPerformOrdinalLookup(ImporterSettings settings) {
|
||||
return OptionUtils.getOption(ORDINAL_LOOKUP_OPTION_NAME, settings.options(),
|
||||
ORDINAL_LOOKUP_OPTION_DEFAULT);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,9 +51,9 @@ abstract class AbstractPeDebugLoader extends AbstractOrdinalSupportLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
List<Option> list =
|
||||
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
List<Option> list = super.getDefaultOptions(provider, loadSpec, domainObject,
|
||||
loadIntoProgram, mirrorFsLayout);
|
||||
list.add(new Option(SHOW_LINE_NUMBERS_OPTION_NAME, SHOW_LINE_NUMBERS_OPTION_DEFAULT,
|
||||
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-showDebugLineNumbers"));
|
||||
return list;
|
||||
|
|
|
@ -19,16 +19,13 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import ghidra.app.plugin.processors.generic.MemoryBlockDefinition;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.OptionUtils;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.formats.gfilesystem.FSRL;
|
||||
import ghidra.formats.gfilesystem.FSUtilities;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.database.function.OverlappingFunctionException;
|
||||
|
@ -67,33 +64,14 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||
* {@link Program}s with {@link Loaded#close()} when they are no longer needed.
|
||||
*
|
||||
* @param provider The bytes to load.
|
||||
* @param loadedName A suggested name for the primary {@link Loaded} {@link Program}.
|
||||
* This is just a suggestion, and a {@link Loader} implementation reserves the right to change
|
||||
* 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 options The load options.
|
||||
* @param log The message log.
|
||||
* @param consumer A consumer object for generated {@link Program}s.
|
||||
* @param monitor A task monitor.
|
||||
* @param settings The {@link Loader.ImporterSettings}.
|
||||
* @return A {@link List} of one or more {@link Loaded} {@link Program}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 CancelledException if the user cancelled the load.
|
||||
*/
|
||||
protected abstract List<Loaded<Program>> loadProgram(ByteProvider provider, String loadedName,
|
||||
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||
protected abstract List<Loaded<Program>> loadProgram(ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException;
|
||||
|
||||
/**
|
||||
|
@ -102,40 +80,32 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
* <p>
|
||||
* NOTE: The loading that occurs in this method will automatically be done in a transaction.
|
||||
*
|
||||
* @param provider The bytes to load into the {@link Program}.
|
||||
* @param loadSpec The {@link LoadSpec} to use during load.
|
||||
* @param options The load options.
|
||||
* @param messageLog The message log.
|
||||
* @param program The {@link Program} to load into.
|
||||
* @param monitor A cancelable task monitor.
|
||||
* @param settings The {@link Loader.ImporterSettings}.
|
||||
* @throws LoadException if the load failed in an expected way.
|
||||
* @throws IOException if there was an IO-related problem loading.
|
||||
* @throws CancelledException if the user cancelled the load.
|
||||
*/
|
||||
protected abstract void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||
List<Option> options, MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||
protected abstract void loadProgramInto(Program program, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException;
|
||||
|
||||
@Override
|
||||
public final LoadResults<? extends DomainObject> load(ByteProvider provider, String loadedName,
|
||||
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog messageLog, Object consumer, TaskMonitor monitor) throws IOException,
|
||||
CancelledException, VersionException, LoadException {
|
||||
public final LoadResults<? extends DomainObject> load(ImporterSettings settings)
|
||||
throws IOException, CancelledException, VersionException, LoadException {
|
||||
|
||||
if (!loadSpec.isComplete()) {
|
||||
if (!settings.loadSpec().isComplete()) {
|
||||
throw new LoadException("Load spec is incomplete");
|
||||
}
|
||||
|
||||
List<Loaded<Program>> loadedPrograms = loadProgram(provider, loadedName, project,
|
||||
projectFolderPath, loadSpec, options, messageLog, consumer, monitor);
|
||||
List<Loaded<Program>> loadedPrograms = loadProgram(settings);
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
||||
monitor.checkCancelled();
|
||||
settings.monitor().checkCancelled();
|
||||
Program program = loadedProgram.getDomainObject(this);
|
||||
try {
|
||||
applyProcessorLabels(options, program);
|
||||
applyProcessorLabels(settings.options(), program);
|
||||
program.setEventsEnabled(true);
|
||||
}
|
||||
finally {
|
||||
|
@ -144,7 +114,7 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
}
|
||||
|
||||
// Subclasses can perform custom post-load fix-ups
|
||||
postLoadProgramFixups(loadedPrograms, project, loadSpec, options, messageLog, monitor);
|
||||
postLoadProgramFixups(loadedPrograms, settings);
|
||||
|
||||
// Discard temporary programs
|
||||
Iterator<Loaded<Program>> iter = loadedPrograms.iterator();
|
||||
|
@ -168,11 +138,10 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public final void loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||
public final void loadInto(Program program, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
|
||||
if (!loadSpec.isComplete()) {
|
||||
if (!settings.loadSpec().isComplete()) {
|
||||
throw new LoadException("Load spec is incomplete");
|
||||
}
|
||||
|
||||
|
@ -180,7 +149,7 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
int transactionID = program.startTransaction("Loading - " + getName());
|
||||
boolean success = false;
|
||||
try {
|
||||
loadProgramInto(provider, loadSpec, options, messageLog, program, monitor);
|
||||
loadProgramInto(program, settings);
|
||||
success = true;
|
||||
}
|
||||
finally {
|
||||
|
@ -191,7 +160,7 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean isLoadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
ArrayList<Option> list = new ArrayList<>();
|
||||
list.add(new Option(APPLY_LABELS_OPTION_NAME, shouldApplyProcessorLabelsByDefault(),
|
||||
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-applyLabels"));
|
||||
|
@ -223,17 +192,12 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
* It provides subclasses an opportunity to do follow-on actions to the load.
|
||||
*
|
||||
* @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 loadSpec The {@link LoadSpec} to use during load.
|
||||
* @param options The load options.
|
||||
* @param messageLog The message log.
|
||||
* @param monitor A cancelable task monitor.
|
||||
* @param settings The {@link Loader.ImporterSettings}.
|
||||
* @throws IOException if there was an IO-related problem loading.
|
||||
* @throws CancelledException if the user cancelled the load.
|
||||
*/
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms,
|
||||
ImporterSettings settings) throws CancelledException, IOException {
|
||||
// Default behavior is to do nothing
|
||||
}
|
||||
|
||||
|
@ -261,21 +225,6 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins the given path elements to form a single path. Empty and null path elements
|
||||
* are ignored. The returned path's separators are converted to unix-style and
|
||||
* windows-specific characters like {@code :} are stripped out, making the path suitable
|
||||
* to be a project path
|
||||
*
|
||||
* @param pathElements The path elements to append to one another
|
||||
* @return A single path consisting of the given path elements appended together
|
||||
* @see FSUtilities#appendPath(String...)
|
||||
*/
|
||||
protected String joinPaths(String... pathElements) {
|
||||
String str = FSUtilities.appendPath(pathElements);
|
||||
return str != null ? FilenameUtils.separatorsToUnix(str).replaceAll(":", "") : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a block name.
|
||||
*
|
||||
|
@ -302,28 +251,26 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
/**
|
||||
* Creates a {@link Program} with the specified attributes.
|
||||
*
|
||||
* @param provider The bytes that will make up the {@link Program}.
|
||||
* @param domainFileName The name for the DomainFile that will store the {@link Program}.
|
||||
* @param imageBase The image base address of the {@link Program}.
|
||||
* @param executableFormatName The file format name of the {@link Program}. Typically this will
|
||||
* be the {@link Loader} name.
|
||||
* @param language The {@link Language} of the {@link Program}.
|
||||
* @param compilerSpec The {@link CompilerSpec} of the {@link Program}.
|
||||
* @param consumer A consumer object for the {@link Program} generated.
|
||||
* @param settings The {@link Loader.ImporterSettings}.
|
||||
* @return The newly created {@link Program}.
|
||||
* @throws IOException if there was an IO-related problem with creating the {@link Program}.
|
||||
*/
|
||||
protected Program createProgram(ByteProvider provider, String domainFileName,
|
||||
Address imageBase, String executableFormatName, Language language,
|
||||
CompilerSpec compilerSpec, Object consumer) throws IOException {
|
||||
protected Program createProgram(Address imageBase, ImporterSettings settings)
|
||||
throws IOException {
|
||||
|
||||
String programName = getProgramNameFromSourceData(provider, domainFileName);
|
||||
Program prog = new ProgramDB(programName, language, compilerSpec, consumer);
|
||||
LanguageCompilerSpecPair pair = settings.loadSpec().getLanguageCompilerSpec();
|
||||
Language language = getLanguageService().getLanguage(pair.languageID);
|
||||
CompilerSpec compilerSpec = language.getCompilerSpecByID(pair.compilerSpecID);
|
||||
|
||||
String programName =
|
||||
getProgramNameFromSourceData(settings.provider(), settings.importNameOnly());
|
||||
Program prog = new ProgramDB(programName, language, compilerSpec, settings.consumer());
|
||||
prog.setEventsEnabled(false);
|
||||
int id = prog.startTransaction("Set program properties");
|
||||
boolean success = false;
|
||||
try {
|
||||
setProgramProperties(prog, provider, executableFormatName);
|
||||
setProgramProperties(prog, settings.provider(), getName());
|
||||
try {
|
||||
if (shouldSetImageBase(prog, imageBase)) {
|
||||
prog.setImageBase(imageBase, true);
|
||||
|
@ -339,11 +286,30 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
finally {
|
||||
prog.endTransaction(id, true); // More efficient to commit when program will be discarded
|
||||
if (!success) {
|
||||
prog.release(consumer);
|
||||
prog.release(settings.consumer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Program} with the specified attributes at the {@link LoadSpec}'s desired
|
||||
* image base
|
||||
*
|
||||
* @param settings The {@link Loader.ImporterSettings}.
|
||||
* @return The newly created {@link Program}.
|
||||
* @throws IOException if there was an IO-related problem with creating the {@link Program}.
|
||||
*/
|
||||
protected Program createProgram(ImporterSettings settings) throws IOException {
|
||||
|
||||
Address imageBaseAddr = getLanguageService()
|
||||
.getLanguage(settings.loadSpec().getLanguageCompilerSpec().languageID)
|
||||
.getAddressFactory()
|
||||
.getDefaultAddressSpace()
|
||||
.getAddress(settings.loadSpec().getDesiredImageBase());
|
||||
|
||||
return createProgram(imageBaseAddr, settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a program's Executable Path, Executable Format, MD5, SHA256, and FSRL properties.
|
||||
*
|
||||
|
@ -387,13 +353,14 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
* Creates default memory blocks for the given {@link Program}.
|
||||
*
|
||||
* @param program The {@link Program} to create default memory blocks for.
|
||||
* @param language The {@link Program}s {@link Language}.
|
||||
* @param log The log to use during memory block creation.
|
||||
* @param settings The {@link Loader.ImporterSettings}.
|
||||
*/
|
||||
protected void createDefaultMemoryBlocks(Program program, Language language, MessageLog log) {
|
||||
protected void createDefaultMemoryBlocks(Program program, ImporterSettings settings) {
|
||||
MessageLog log = settings.log();
|
||||
int id = program.startTransaction("Create default blocks");
|
||||
try {
|
||||
|
||||
LanguageCompilerSpecPair pair = settings.loadSpec().getLanguageCompilerSpec();
|
||||
Language language = getLanguageService().getLanguage(pair.languageID);
|
||||
MemoryBlockDefinition[] defaultMemoryBlocks = language.getDefaultMemoryBlocks();
|
||||
if (defaultMemoryBlocks == null) {
|
||||
return;
|
||||
|
@ -423,6 +390,10 @@ public abstract class AbstractProgramLoader implements Loader {
|
|||
}
|
||||
}
|
||||
}
|
||||
catch (LanguageNotFoundException e) {
|
||||
log.appendMsg("Failed get language for: " +
|
||||
settings.loadSpec().getLanguageCompilerSpec().languageID);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(id, true);
|
||||
}
|
||||
|
|
|
@ -18,15 +18,9 @@ package ghidra.app.util.opinion;
|
|||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* An abstract {@link Loader} that provides a convenience wrapper around
|
||||
|
@ -38,69 +32,51 @@ public abstract class AbstractProgramWrapperLoader extends AbstractProgramLoader
|
|||
/**
|
||||
* Loads bytes in a particular format into the given {@link Program}.
|
||||
*
|
||||
* @param provider The bytes to load.
|
||||
* @param loadSpec The {@link LoadSpec} to use during load.
|
||||
* @param options The load options.
|
||||
* @param program The {@link Program} to load into.
|
||||
* @param monitor A cancelable task monitor.
|
||||
* @param log The message log.
|
||||
* @param settings The {@link Loader.ImporterSettings}.
|
||||
* @throws IOException if there was an IO-related problem loading.
|
||||
* @throws CancelledException if the user cancelled the load.
|
||||
*/
|
||||
protected abstract void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
protected abstract void load(Program program, ImporterSettings settings)
|
||||
throws CancelledException, IOException;
|
||||
|
||||
@Override
|
||||
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||
protected List<Loaded<Program>> loadProgram(ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||
Language language = getLanguageService().getLanguage(pair.languageID);
|
||||
CompilerSpec compilerSpec = language.getCompilerSpecByID(pair.compilerSpecID);
|
||||
|
||||
Address imageBaseAddr = language.getAddressFactory()
|
||||
.getDefaultAddressSpace()
|
||||
.getAddress(loadSpec.getDesiredImageBase());
|
||||
|
||||
Program program = createProgram(provider, programName, imageBaseAddr, getName(), language,
|
||||
compilerSpec, consumer);
|
||||
List<Loaded<Program>> loadedList = List.of(
|
||||
new Loaded<Program>(program, programName, project, programFolderPath, consumer));
|
||||
Program program = createProgram(settings);
|
||||
Loaded<Program> loaded = new Loaded<Program>(program, settings);
|
||||
|
||||
int transactionID = program.startTransaction("Loading");
|
||||
boolean success = false;
|
||||
try {
|
||||
load(provider, loadSpec, options, program, monitor, log);
|
||||
createDefaultMemoryBlocks(program, language, log);
|
||||
load(program, settings);
|
||||
createDefaultMemoryBlocks(program, settings);
|
||||
success = true;
|
||||
return loadedList;
|
||||
return List.of(loaded);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(transactionID, true); // More efficient to commit when program will be discarded
|
||||
if (!success) {
|
||||
loadedList.forEach(Loaded::close);
|
||||
loaded.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||
List<Option> options, MessageLog log, Program program, TaskMonitor monitor)
|
||||
protected void loadProgramInto(Program program, ImporterSettings settings)
|
||||
throws CancelledException, LoadException, IOException {
|
||||
|
||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||
LanguageCompilerSpecPair pair = settings.loadSpec().getLanguageCompilerSpec();
|
||||
LanguageID languageID = program.getLanguageID();
|
||||
CompilerSpecID compilerSpecID = program.getCompilerSpec().getCompilerSpecID();
|
||||
if (!(pair.languageID.equals(languageID) && pair.compilerSpecID.equals(compilerSpecID))) {
|
||||
String message = provider.getAbsolutePath() +
|
||||
String message = settings.provider().getAbsolutePath() +
|
||||
" does not have the same language/compiler spec as program " + program.getName();
|
||||
log.appendMsg(message);
|
||||
settings.log().appendMsg(message);
|
||||
throw new LoadException(message);
|
||||
}
|
||||
load(provider, loadSpec, options, program, monitor, log);
|
||||
load(program, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,7 +22,6 @@ import ghidra.app.util.*;
|
|||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
|
@ -31,7 +30,6 @@ import ghidra.program.model.mem.Memory;
|
|||
import ghidra.util.Msg;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class BinaryLoader extends AbstractProgramLoader {
|
||||
|
||||
|
@ -269,55 +267,48 @@ public class BinaryLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||
protected List<Loaded<Program>> loadProgram(ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||
LanguageCompilerSpecPair pair = settings.loadSpec().getLanguageCompilerSpec();
|
||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||
CompilerSpec importerCompilerSpec =
|
||||
importerLanguage.getCompilerSpecByID(pair.compilerSpecID);
|
||||
|
||||
Address baseAddr =
|
||||
importerLanguage.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
Program prog = createProgram(provider, programName, baseAddr, getName(), importerLanguage,
|
||||
importerCompilerSpec, consumer);
|
||||
List<Loaded<Program>> loadedList =
|
||||
List.of(new Loaded<>(prog, programName, project, programFolderPath, consumer));
|
||||
|
||||
Program prog = createProgram(baseAddr, settings);
|
||||
Loaded<Program> loaded = new Loaded<Program>(prog, settings);
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
loadInto(provider, loadSpec, options, log, prog, monitor);
|
||||
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
||||
loadInto(prog, settings);
|
||||
createDefaultMemoryBlocks(prog, settings);
|
||||
success = true;
|
||||
return loadedList;
|
||||
return List.of(loaded);
|
||||
}
|
||||
finally {
|
||||
if (!success) {
|
||||
loadedList.forEach(Loaded::close);
|
||||
loaded.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
||||
protected void loadProgramInto(Program prog, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
long length = getLength(options);
|
||||
long length = getLength(settings.options());
|
||||
//File file = provider.getFile();
|
||||
long fileOffset = getFileOffset(options);
|
||||
Address baseAddr = getBaseAddr(options);
|
||||
String blockName = getBlockName(options);
|
||||
boolean isOverlay = isOverlay(options);
|
||||
long fileOffset = getFileOffset(settings.options());
|
||||
Address baseAddr = getBaseAddr(settings.options());
|
||||
String blockName = getBlockName(settings.options());
|
||||
boolean isOverlay = isOverlay(settings.options());
|
||||
|
||||
if (length == 0) {
|
||||
length = provider.length();
|
||||
length = settings.provider().length();
|
||||
}
|
||||
|
||||
length = clipToMemorySpace(length, log, prog);
|
||||
length = clipToMemorySpace(length, settings.log(), prog);
|
||||
|
||||
FileBytes fileBytes =
|
||||
MemoryBlockUtils.createFileBytes(prog, provider, fileOffset, length, monitor);
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(prog, settings.provider(),
|
||||
fileOffset, length, settings.monitor());
|
||||
try {
|
||||
AddressSpace space = prog.getAddressFactory().getDefaultAddressSpace();
|
||||
if (baseAddr == null) {
|
||||
|
@ -326,7 +317,7 @@ public class BinaryLoader extends AbstractProgramLoader {
|
|||
if (blockName == null || blockName.length() == 0) {
|
||||
blockName = generateBlockName(prog, isOverlay, baseAddr.getAddressSpace());
|
||||
}
|
||||
createBlock(prog, isOverlay, blockName, baseAddr, fileBytes, length, log);
|
||||
createBlock(prog, isOverlay, blockName, baseAddr, fileBytes, length, settings.log());
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new LoadException("Invalid address range specified: start:" + baseAddr +
|
||||
|
@ -359,7 +350,7 @@ public class BinaryLoader extends AbstractProgramLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
long fileOffset = 0;
|
||||
long origFileLength = -1;
|
||||
try {
|
||||
|
@ -404,7 +395,8 @@ public class BinaryLoader extends AbstractProgramLoader {
|
|||
list.add(new Option(OPTION_NAME_LEN, new HexLong(length), HexLong.class,
|
||||
Loader.COMMAND_LINE_ARG_PREFIX + "-length"));
|
||||
|
||||
list.addAll(super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram));
|
||||
list.addAll(super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram,
|
||||
mirrorFsLayout));
|
||||
return list;
|
||||
}
|
||||
|
||||
|
|
|
@ -123,9 +123,9 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
List<Option> list =
|
||||
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
List<Option> list = super.getDefaultOptions(provider, loadSpec, domainObject,
|
||||
loadIntoProgram, mirrorFsLayout);
|
||||
if (!loadIntoProgram) {
|
||||
list.add(new Option(FAKE_LINK_OPTION_NAME, FAKE_LINK_OPTION_DEFAULT));
|
||||
}
|
||||
|
@ -162,21 +162,23 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
protected void load(Program program, ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
|
||||
try {
|
||||
CoffFileHeader header = new CoffFileHeader(provider);
|
||||
CoffFileHeader header = new CoffFileHeader(settings.provider());
|
||||
header.parse(monitor);
|
||||
|
||||
Map<CoffSectionHeader, Address> sectionsMap = new HashMap<>();
|
||||
Map<CoffSymbol, Symbol> symbolsMap = new HashMap<>();
|
||||
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
processSectionHeaders(provider, header, program, fileBytes, monitor, log, sectionsMap,
|
||||
performFakeLinking(options));
|
||||
FileBytes fileBytes =
|
||||
MemoryBlockUtils.createFileBytes(program, settings.provider(), monitor);
|
||||
processSectionHeaders(settings.provider(), header, program, fileBytes, monitor, log,
|
||||
sectionsMap, performFakeLinking(settings.options()));
|
||||
processSymbols(header, program, monitor, log, sectionsMap, symbolsMap);
|
||||
processEntryPoint(header, program, monitor, log);
|
||||
processRelocations(header, program, sectionsMap, symbolsMap, log, monitor);
|
||||
|
|
|
@ -19,15 +19,12 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.RandomAccessByteProvider;
|
||||
import ghidra.app.util.bin.format.pe.*;
|
||||
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* An opinion service for processing Microsoft DBG files.
|
||||
|
@ -68,15 +65,14 @@ public class DbgLoader extends AbstractPeDebugLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program prog,
|
||||
TaskMonitor monitor, MessageLog log) throws IOException {
|
||||
public void load(Program prog, ImporterSettings settings) throws IOException {
|
||||
|
||||
if (!prog.getExecutableFormat().equals(PeLoader.PE_NAME)) {
|
||||
throw new IOException("Loading of DBG file may only be 'added' to existing " +
|
||||
PeLoader.PE_NAME + " Program");
|
||||
}
|
||||
|
||||
SeparateDebugHeader debug = new SeparateDebugHeader(provider);
|
||||
SeparateDebugHeader debug = new SeparateDebugHeader(settings.provider());
|
||||
|
||||
String parentPath = prog.getExecutablePath();
|
||||
File parentFile = new File(parentPath);
|
||||
|
@ -93,8 +89,8 @@ public class DbgLoader extends AbstractPeDebugLoader {
|
|||
sectionToAddress.put(sectionHeader,
|
||||
imageBase.add(sectionHeader.getVirtualAddress()));
|
||||
}
|
||||
processDebug(debug.getParser(), parentPE.getNTHeader(), sectionToAddress, prog, options,
|
||||
monitor);
|
||||
processDebug(debug.getParser(), parentPE.getNTHeader(), sectionToAddress, prog,
|
||||
settings.options(), settings.monitor());
|
||||
}
|
||||
finally {
|
||||
if (provider2 != null) {
|
||||
|
|
|
@ -24,7 +24,6 @@ import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
|||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -101,31 +100,16 @@ public class DecompileDebugXmlLoader extends AbstractProgramLoader {
|
|||
/**
|
||||
* After initial parsing of the XML file, load the details into a new program for
|
||||
* loading/viewing in Ghidra.
|
||||
*
|
||||
* @param provider ByteProvider
|
||||
* @param programName Program name from the XML
|
||||
* @param project active project
|
||||
* @param programFolderPath folder path to XML
|
||||
* @param loadSpec String parsed out of the XML for details regarding language/compiler spec
|
||||
* @param options program options - will always be empty
|
||||
* @param log MessageLog
|
||||
* @param consumer Object
|
||||
* @param monitor TaskMonitor
|
||||
*
|
||||
* @return List of loaded programs - should always be 1
|
||||
* <hr>
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||
Project project,
|
||||
String programFolderPath, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
||||
Object consumer,
|
||||
TaskMonitor monitor) throws IOException, CancelledException {
|
||||
protected List<Loaded<Program>> loadProgram(ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||
LanguageCompilerSpecPair pair = settings.loadSpec().getLanguageCompilerSpec();
|
||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||
CompilerSpec importerCompilerSpec =
|
||||
importerLanguage.getCompilerSpecByID(pair.compilerSpecID);
|
||||
ParseGhidraDebugResult parsedResult = parse(provider);
|
||||
ParseGhidraDebugResult parsedResult = parse(settings.provider());
|
||||
if (parsedResult.lastInfo == null) {
|
||||
return new ArrayList<Loaded<Program>>();
|
||||
}
|
||||
|
@ -136,15 +120,14 @@ public class DecompileDebugXmlLoader extends AbstractProgramLoader {
|
|||
importerLanguage.getAddressFactory().getAddress(parsedResult.lastInfo.offset());
|
||||
}
|
||||
|
||||
Program prog = createProgram(provider, programName, imageBase, getName(),
|
||||
importerLanguage, importerCompilerSpec, consumer);
|
||||
List<Loaded<Program>> loadedList =
|
||||
List.of(new Loaded<>(prog, programName, project, programFolderPath, consumer));
|
||||
Program prog = createProgram(imageBase, settings);
|
||||
List<Loaded<Program>> loadedList = List.of(new Loaded<>(prog, settings));
|
||||
|
||||
int loadingId = prog.startTransaction("Loading debug XML file");
|
||||
|
||||
try {
|
||||
doImport(parsedResult.debugXmlMgr, options, log, prog, monitor, false, programName);
|
||||
doImport(parsedResult.debugXmlMgr, settings.options(), settings.log(), prog,
|
||||
settings.monitor(), false, settings.importName());
|
||||
}
|
||||
catch (
|
||||
|
||||
|
@ -304,8 +287,7 @@ public class DecompileDebugXmlLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||
protected void loadProgramInto(Program program, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
// since we will not ever be loading this debug program into an existing program, this
|
||||
// should not be used.
|
||||
|
|
|
@ -19,13 +19,10 @@ import java.io.*;
|
|||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A {@link Loader} for processing Microsoft DEF files.
|
||||
|
@ -80,16 +77,15 @@ public class DefLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program prog,
|
||||
TaskMonitor monitor, MessageLog log) throws IOException {
|
||||
public void load(Program prog, ImporterSettings settings) throws IOException {
|
||||
|
||||
if (!prog.getExecutableFormat().equals(PeLoader.PE_NAME)) {
|
||||
throw new IOException("Program must be a " + PeLoader.PE_NAME);
|
||||
}
|
||||
|
||||
SymbolTable symtab = prog.getSymbolTable();
|
||||
Consumer<String> errorConsumer = err -> log.appendMsg("DefLoader", err);
|
||||
for (DefExportLine def : parseExports(provider)) {
|
||||
Consumer<String> errorConsumer = err -> settings.log().appendMsg("DefLoader", err);
|
||||
for (DefExportLine def : parseExports(settings.provider())) {
|
||||
Integer ordinal = def.getOrdinal();
|
||||
if (ordinal == null) {
|
||||
continue;
|
||||
|
@ -105,7 +101,7 @@ public class DefLoader extends AbstractProgramWrapperLoader {
|
|||
label.setPrimary();
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
log.appendMsg(e.getMessage());
|
||||
settings.log().appendMsg(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,9 @@ import ghidra.app.util.bin.BinaryReader;
|
|||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldArchitecture;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldCacheHeader;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A {@link Loader} for DYLD shared cache files.
|
||||
|
@ -129,13 +127,12 @@ public class DyldCacheLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
|
||||
public void load(Program program, ImporterSettings settings) throws IOException {
|
||||
|
||||
try {
|
||||
DyldCacheProgramBuilder.buildProgram(program, provider,
|
||||
MemoryBlockUtils.createFileBytes(program, provider, monitor),
|
||||
getDyldCacheOptions(options), log, monitor);
|
||||
DyldCacheProgramBuilder.buildProgram(program, settings.provider(),
|
||||
MemoryBlockUtils.createFileBytes(program, settings.provider(), settings.monitor()),
|
||||
getDyldCacheOptions(settings.options()), settings.log(), settings.monitor());
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return;
|
||||
|
@ -147,9 +144,9 @@ public class DyldCacheLoader extends AbstractProgramWrapperLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
List<Option> list =
|
||||
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
List<Option> list = super.getDefaultOptions(provider, loadSpec, domainObject,
|
||||
loadIntoProgram, mirrorFsLayout);
|
||||
if (!loadIntoProgram) {
|
||||
list.add(new Option(FIXUP_SLIDE_POINTERS_OPTION_NAME,
|
||||
FIXUP_SLIDE_POINTERS_OPTION_DEFAULT, Boolean.class,
|
||||
|
|
|
@ -22,8 +22,8 @@ import ghidra.app.util.Option;
|
|||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.elf.ElfException;
|
||||
import ghidra.app.util.bin.format.elf.ElfHeader;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.ProjectData;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -31,7 +31,6 @@ import ghidra.program.util.ExternalSymbolResolver;
|
|||
import ghidra.util.Msg;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A {@link Loader} for processing executable and linking files (ELF).
|
||||
|
@ -65,12 +64,12 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
|
||||
// NOTE: add-to-program is not supported
|
||||
|
||||
List<Option> options =
|
||||
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
|
||||
List<Option> options = super.getDefaultOptions(provider, loadSpec, domainObject,
|
||||
loadIntoProgram, mirrorFsLayout);
|
||||
|
||||
try {
|
||||
ElfLoaderOptionsFactory.addOptions(options, provider, loadSpec);
|
||||
|
@ -138,13 +137,14 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
public void load(Program program, ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
try {
|
||||
ElfHeader elf = new ElfHeader(provider, msg -> log.appendMsg(msg));
|
||||
ElfProgramBuilder.loadElf(elf, program, options, log, monitor);
|
||||
ElfHeader elf =
|
||||
new ElfHeader(settings.provider(), msg -> settings.log().appendMsg(msg));
|
||||
ElfProgramBuilder.loadElf(elf, program, settings.options(), settings.log(),
|
||||
settings.monitor());
|
||||
}
|
||||
catch (ElfException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
|
@ -152,17 +152,17 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
super.postLoadProgramFixups(loadedPrograms, project, loadSpec, options, messageLog,
|
||||
monitor);
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms,
|
||||
ImporterSettings settings) throws CancelledException, IOException {
|
||||
super.postLoadProgramFixups(loadedPrograms, settings);
|
||||
|
||||
ProjectData projectData = project != null ? project.getProjectData() : null;
|
||||
try (ExternalSymbolResolver esr = new ExternalSymbolResolver(projectData, monitor)) {
|
||||
ProjectData projectData =
|
||||
settings.project() != null ? settings.project().getProjectData() : null;
|
||||
try (ExternalSymbolResolver esr =
|
||||
new ExternalSymbolResolver(projectData, settings.monitor())) {
|
||||
loadedPrograms.forEach(p -> esr.addProgramToFixup(p));
|
||||
esr.fixUnresolvedExternalSymbols();
|
||||
esr.logInfo(messageLog::appendMsg, true);
|
||||
esr.logInfo(settings.log()::appendMsg, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,11 +23,9 @@ import org.apache.commons.io.FilenameUtils;
|
|||
import db.DBHandle;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.data.OpenMode;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.framework.store.db.PackedDatabase;
|
||||
import ghidra.framework.store.local.ItemSerializer;
|
||||
import ghidra.program.database.DataTypeArchiveContentHandler;
|
||||
|
@ -48,19 +46,17 @@ public class GdtLoader implements Loader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadResults<? extends DomainObject> load(ByteProvider provider, String filename,
|
||||
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog messageLog, Object consumer, TaskMonitor monitor)
|
||||
public LoadResults<? extends DomainObject> load(ImporterSettings settings)
|
||||
throws IOException, CancelledException, VersionException {
|
||||
|
||||
DataTypeArchive dtArchive =
|
||||
loadPackedProgramDatabase(provider, filename, consumer, monitor);
|
||||
return new LoadResults<>(dtArchive, filename, project, projectFolderPath, consumer);
|
||||
DataTypeArchive dtArchive = loadPackedProgramDatabase(settings.provider(),
|
||||
settings.importName(), settings.consumer(), settings.monitor());
|
||||
return new LoadResults<>(new Loaded<>(dtArchive, settings));
|
||||
}
|
||||
|
||||
private DataTypeArchive loadPackedProgramDatabase(ByteProvider provider, String programName,
|
||||
|
@ -109,8 +105,7 @@ public class GdtLoader implements Loader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||
public void loadInto(Program program, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
throw new LoadException("Cannot add GDT to program");
|
||||
}
|
||||
|
|
|
@ -23,11 +23,9 @@ import org.apache.commons.io.FilenameUtils;
|
|||
import db.DBHandle;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.data.OpenMode;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.framework.store.db.PackedDatabase;
|
||||
import ghidra.framework.store.local.ItemSerializer;
|
||||
import ghidra.program.database.ProgramContentHandler;
|
||||
|
@ -67,18 +65,17 @@ public class GzfLoader implements Loader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadResults<? extends DomainObject> load(ByteProvider provider, String programName,
|
||||
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog messageLog, Object consumer, TaskMonitor monitor)
|
||||
public LoadResults<? extends DomainObject> load(ImporterSettings settings)
|
||||
throws IOException, CancelledException, VersionException {
|
||||
|
||||
Program program = loadPackedProgramDatabase(provider, programName, consumer, monitor);
|
||||
return new LoadResults<>(program, programName, project, projectFolderPath, consumer);
|
||||
Program program = loadPackedProgramDatabase(settings.provider(), settings.importName(),
|
||||
settings.consumer(), settings.monitor());
|
||||
return new LoadResults<>(new Loaded<>(program, settings));
|
||||
}
|
||||
|
||||
private Program loadPackedProgramDatabase(ByteProvider provider, String programName,
|
||||
|
@ -127,8 +124,7 @@ public class GzfLoader implements Loader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||
public void loadInto(Program program, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
throw new LoadException("Cannot add GZF to program");
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import ghidra.app.util.Option;
|
|||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -75,7 +74,8 @@ public class IntelHexLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
|
||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program) {
|
||||
Address baseAddr = null;
|
||||
|
||||
for (Option option : options) {
|
||||
|
@ -142,23 +142,15 @@ public class IntelHexLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||
protected List<Loaded<Program>> loadProgram(ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||
CompilerSpec importerCompilerSpec =
|
||||
importerLanguage.getCompilerSpecByID(pair.compilerSpecID);
|
||||
|
||||
Program prog = createProgram(provider, programName, null, getName(), importerLanguage,
|
||||
importerCompilerSpec, consumer);
|
||||
List<Loaded<Program>> loadedList =
|
||||
List.of(new Loaded<>(prog, programName, project, programFolderPath, consumer));
|
||||
Program prog = createProgram(null, settings);
|
||||
List<Loaded<Program>> loadedList = List.of(new Loaded<>(prog, settings));
|
||||
boolean success = false;
|
||||
try {
|
||||
loadInto(provider, loadSpec, options, log, prog, monitor);
|
||||
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
||||
loadInto(prog, settings);
|
||||
createDefaultMemoryBlocks(prog, settings);
|
||||
success = true;
|
||||
return loadedList;
|
||||
}
|
||||
|
@ -170,16 +162,16 @@ public class IntelHexLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
||||
protected void loadProgramInto(Program prog, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
Address baseAddr = getBaseAddr(options);
|
||||
Address baseAddr = getBaseAddr(settings.options());
|
||||
|
||||
if (baseAddr == null) {
|
||||
baseAddr = prog.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
}
|
||||
try {
|
||||
processIntelHex(provider, options, log, prog, monitor);
|
||||
processIntelHex(settings.provider(), settings.options(), settings.log(), prog,
|
||||
settings.monitor());
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new LoadException(
|
||||
|
@ -265,7 +257,7 @@ public class IntelHexLoader extends AbstractProgramLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
String blockName = "";
|
||||
boolean isOverlay = false;
|
||||
Address baseAddr = null;
|
||||
|
|
|
@ -16,15 +16,9 @@
|
|||
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}
|
||||
* Thrown when a {@link Loader#load(ghidra.app.util.opinion.Loader.ImporterSettings) load}
|
||||
* fails in an expected way. The supplied message should explain the reason.
|
||||
*/
|
||||
public class LoadException extends IOException {
|
||||
|
|
|
@ -19,16 +19,16 @@ import java.io.IOException;
|
|||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.util.InvalidNameException;
|
||||
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.
|
||||
* {@link Loader#load(ghidra.app.util.opinion.Loader.ImporterSettings)}. 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
|
||||
*/
|
||||
|
@ -54,29 +54,14 @@ public class LoadResults<T extends DomainObject> implements Iterable<Loaded<T>>,
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates 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}.
|
||||
* Creates a new {@link LoadResults} that contains the given {@link Loaded}
|
||||
* {@link DomainObject}. This {@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(TaskMonitor) save} occurs, this will attempted to be used for the resulting
|
||||
* {@link DomainFile}'s name.
|
||||
* @param project If not null, the project this will get saved to during a
|
||||
* {@link #save(TaskMonitor)} operation
|
||||
* @param projectFolderPath The project folder path this will get saved to during a
|
||||
* {@link #save(TaskMonitor) save} operation. If null or empty, the root project folder will
|
||||
* be used.
|
||||
* @param consumer A reference to the object "consuming" the returned this
|
||||
* {@link LoadResults}, used to ensure the underlying {@link DomainObject}s are only closed
|
||||
* when every consumer is done with it (see {@link #close()}). NOTE: Wrapping a
|
||||
* {@link DomainObject} in a {@link LoadResults} transfers responsibility of releasing the
|
||||
* given {@link DomainObject} to this {@link LoadResults}'s {@link #close()} method.
|
||||
* @param loaded The {@link Loaded} {@link DomainObject}
|
||||
*/
|
||||
public LoadResults(T domainObject, String name, Project project, String projectFolderPath,
|
||||
Object consumer) {
|
||||
this(List.of(new Loaded<T>(domainObject, name, project, projectFolderPath, consumer)));
|
||||
public LoadResults(Loaded<T> loaded) {
|
||||
this(List.of(loaded));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -155,9 +140,11 @@ public class LoadResults<T extends DomainObject> implements Iterable<Loaded<T>>,
|
|||
* @throws IOException If there was a problem saving. A thrown exception may result in only some
|
||||
* of the {@link Loaded} elements being saved. It is the responsibility of the caller to clean
|
||||
* things up appropriately.
|
||||
* @throws InvalidNameException if saving with an invalid name
|
||||
* @see Loaded#save(TaskMonitor)
|
||||
*/
|
||||
public void save(TaskMonitor monitor) throws CancelledException, IOException {
|
||||
public void save(TaskMonitor monitor)
|
||||
throws CancelledException, IOException, InvalidNameException {
|
||||
for (Loaded<T> loaded : loadedList) {
|
||||
loaded.save(monitor);
|
||||
}
|
||||
|
|
|
@ -15,13 +15,26 @@
|
|||
*/
|
||||
package ghidra.app.util.opinion;
|
||||
|
||||
import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import ghidra.app.util.opinion.Loader.ImporterSettings;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
|
||||
import ghidra.framework.data.FolderLinkContentHandler;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.program.database.ProgramLinkContentHandler;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -36,8 +49,10 @@ public class Loaded<T extends DomainObject> implements AutoCloseable {
|
|||
|
||||
protected final T domainObject;
|
||||
protected final String name;
|
||||
protected FSRL fsrl;
|
||||
protected Project project;
|
||||
protected String projectFolderPath;
|
||||
protected String projectRootPath;
|
||||
protected boolean mirrorFsLayout;
|
||||
protected Object loadedConsumer;
|
||||
|
||||
protected DomainFile domainFile;
|
||||
|
@ -48,26 +63,44 @@ public class Loaded<T extends DomainObject> implements AutoCloseable {
|
|||
* This object needs to be {@link #close() closed} when done with it.
|
||||
*
|
||||
* @param domainObject The loaded {@link DomainObject}
|
||||
* @param name The name of the loaded {@link DomainObject}. If a {@link #save(TaskMonitor)}
|
||||
* occurs, this will attempted to be used for the resulting {@link DomainFile}'s name.
|
||||
* @param project If not null, the project this will get saved to during a
|
||||
* @param name The name of the loaded {@link DomainObject}. Path information that appears at the
|
||||
* beginning the name will be appended to the {@code projectRootPath} during a
|
||||
* {@link #save(TaskMonitor) operation}.
|
||||
* @param fsrl The {@link FSRL} of the loaded {@link DomainObject}
|
||||
* @param project If not {@code null}, the project this will get saved to during a
|
||||
* {@link #save(TaskMonitor)} operation
|
||||
* @param projectFolderPath The project folder path this will get saved to during a
|
||||
* {@link #save(TaskMonitor)} operation. If null or empty, the root project folder will be
|
||||
* used.
|
||||
* @param projectRootPath The project folder path that all {@link Loaded} {@link DomainObject}s
|
||||
* will be {@link #save(TaskMonitor) saved} relative to. If {@code null}, "/" will be used.
|
||||
* @param mirrorFsLayout True if the filesystem layout should be mirrored when
|
||||
* {@link #save(TaskMonitor) saving}; otherwise, false
|
||||
* @param consumer A reference to the object "consuming" the returned {@link Loaded}
|
||||
* {@link DomainObject}, used to ensure the underlying {@link DomainObject} is only closed
|
||||
* when every consumer is done with it (see {@link #close()}). NOTE: Wrapping a
|
||||
* {@link DomainObject} in a {@link Loaded} transfers responsibility of releasing the
|
||||
* given {@link DomainObject} to this {@link Loaded}'s {@link #close()} method.
|
||||
*/
|
||||
public Loaded(T domainObject, String name, Project project, String projectFolderPath,
|
||||
Object consumer) {
|
||||
public Loaded(T domainObject, String name, FSRL fsrl, Project project, String projectRootPath,
|
||||
boolean mirrorFsLayout, Object consumer) {
|
||||
this.domainObject = domainObject;
|
||||
this.name = name;
|
||||
this.fsrl = fsrl;
|
||||
this.project = project;
|
||||
this.mirrorFsLayout = mirrorFsLayout;
|
||||
this.loadedConsumer = consumer;
|
||||
setProjectFolderPath(projectFolderPath);
|
||||
setProjectFolderPath(projectRootPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Loaded} object.
|
||||
* <p>
|
||||
* This object needs to be {@link #close() closed} when done with it.
|
||||
*
|
||||
* @param domainObject The loaded {@link DomainObject}
|
||||
* @param settings The {@link Loader.ImporterSettings}.
|
||||
*/
|
||||
public Loaded(T domainObject, ImporterSettings settings) {
|
||||
this(domainObject, settings.importName(), settings.provider().getFSRL(), settings.project(),
|
||||
settings.projectRootPath(), settings.mirrorFsLayout(), settings.consumer());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,25 +198,26 @@ public class Loaded<T extends DomainObject> implements AutoCloseable {
|
|||
* @return the project folder path
|
||||
*/
|
||||
public String getProjectFolderPath() {
|
||||
return projectFolderPath;
|
||||
return projectRootPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the project folder path this will get saved to during a {@link #save(TaskMonitor)}
|
||||
* operation.
|
||||
*
|
||||
* @param projectFolderPath The project folder path this will get saved to during a
|
||||
* {@link #save(TaskMonitor)} operation. If null or empty, the root project folder will be
|
||||
* used.
|
||||
* @param projectRootPath The project folder path that all {@link Loaded} {@link DomainObject}s
|
||||
* will be saved relative to. If {@code null}, "/" will be used.
|
||||
*/
|
||||
public void setProjectFolderPath(String projectFolderPath) {
|
||||
if (projectFolderPath == null || projectFolderPath.isBlank()) {
|
||||
projectFolderPath = "/";
|
||||
public void setProjectFolderPath(String projectRootPath) {
|
||||
if (projectRootPath == null || projectRootPath.isBlank()) {
|
||||
projectRootPath = "/";
|
||||
}
|
||||
else if (!projectFolderPath.endsWith("/")) {
|
||||
projectFolderPath += "/";
|
||||
else if (!projectRootPath.endsWith("/")) {
|
||||
projectRootPath += "/";
|
||||
}
|
||||
this.projectFolderPath = projectFolderPath;
|
||||
|
||||
this.projectRootPath =
|
||||
mirrorFsLayout ? FSUtilities.mirroredProjectPath(projectRootPath) : projectRootPath;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,11 +233,16 @@ public class Loaded<T extends DomainObject> implements AutoCloseable {
|
|||
* @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
|
||||
* @throws IOException If there was an IO-related error, a project wasn't specified, an invalid
|
||||
* name was specified, or it was already successfully saved and still exists
|
||||
* @throws InvalidNameException if saving with an invalid name
|
||||
*/
|
||||
public DomainFile save(TaskMonitor monitor)
|
||||
throws CancelledException, ClosedException, IOException {
|
||||
throws CancelledException, ClosedException, IOException, InvalidNameException {
|
||||
|
||||
if (project == null) {
|
||||
throw new IOException("Cannot save to null project");
|
||||
}
|
||||
|
||||
if (domainObject.isClosed()) {
|
||||
throw new ClosedException(
|
||||
|
@ -221,11 +260,16 @@ public class Loaded<T extends DomainObject> implements AutoCloseable {
|
|||
domainFile = null;
|
||||
}
|
||||
|
||||
if (mirrorFsLayout && fsrl != null) {
|
||||
domainFile = mirror(monitor);
|
||||
return domainFile;
|
||||
}
|
||||
|
||||
int uniqueNameIndex = 0;
|
||||
String uniqueName = name;
|
||||
try {
|
||||
DomainFolder programFolder = ProjectDataUtils.createDomainFolderPath(
|
||||
project.getProjectData().getRootFolder(), projectFolderPath);
|
||||
String uniqueName = FilenameUtils.getName(name);
|
||||
DomainFolder programFolder =
|
||||
ProjectDataUtils.createDomainFolderPath(project.getProjectData().getRootFolder(),
|
||||
FSUtilities.appendPath(projectRootPath, FilenameUtils.getFullPath(name)));
|
||||
while (!monitor.isCancelled()) {
|
||||
try {
|
||||
domainFile = programFolder.createFile(uniqueName, domainObject, monitor);
|
||||
|
@ -236,10 +280,7 @@ public class Loaded<T extends DomainObject> implements AutoCloseable {
|
|||
++uniqueNameIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
throw new CancelledException();
|
||||
}
|
||||
|
||||
|
@ -294,4 +335,124 @@ public class Loaded<T extends DomainObject> implements AutoCloseable {
|
|||
public String toString() {
|
||||
return getProjectFolderPath() + getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* A project link and its associated metadata that was created during the mirror process
|
||||
*
|
||||
* @param linkFile The {@link DomainFile project link}. It may link to a {@link DomainFile} or
|
||||
* a {@link DomainFolder}.
|
||||
* @param projectLinkTarget The project path of the link's target
|
||||
* @param symlink The original target value of the link. It may be either relative, or absolute.
|
||||
* @param relative True if the {@code symlink} is relative; false if it is absolute
|
||||
*/
|
||||
private record MirroredLink(DomainFile linkFile, String projectLinkTarget, String symlink,
|
||||
boolean relative) {}
|
||||
|
||||
/**
|
||||
* Saves the loaded {@link DomainObject} to the given {@link Project}, mirroring this object's
|
||||
* filesystem path in the project. Depending on the nature of the filesystem path, project
|
||||
* folder and/or file links may be created during the save.
|
||||
*
|
||||
* @param monitor A cancelable task monitor
|
||||
* @return The {@link DomainFile} where the save happened
|
||||
* @throws CancelledException if the operation was cancelled
|
||||
* @throws IOException If there was an IO-related error
|
||||
* @throws InvalidNameException if saving with an invalid name
|
||||
*/
|
||||
private DomainFile mirror(TaskMonitor monitor)
|
||||
throws IOException, InvalidNameException, CancelledException {
|
||||
DomainFolder mirrorRootProjectFolder = ProjectDataUtils
|
||||
.createDomainFolderPath(project.getProjectData().getRootFolder(), projectRootPath);
|
||||
String currentPath = null;
|
||||
Set<String> processedPaths = new HashSet<>();
|
||||
String[] pathElements = FSUtilities.splitPath(fsrl.getPath());
|
||||
try (RefdFile ref = FileSystemService.getInstance().getRefdFile(fsrl, monitor)) {
|
||||
for (int i = 0; i < pathElements.length; i++) {
|
||||
String pathElement = pathElements[i];
|
||||
if (i == 0) {
|
||||
if (!pathElement.isEmpty()) {
|
||||
throw new IOException("FSRL '%s' is not absolute!".formatted(fsrl));
|
||||
}
|
||||
currentPath = "/";
|
||||
continue;
|
||||
}
|
||||
currentPath = FSUtilities.appendPath(currentPath, pathElement);
|
||||
if (processedPaths.contains(currentPath)) {
|
||||
continue;
|
||||
}
|
||||
GFileSystem fs = ref.fsRef.getFilesystem();
|
||||
GFile currentFile = fs.lookup(currentPath);
|
||||
String currentParentDirPath = currentFile.getParentFile().getPath();
|
||||
DomainFolder parentProjectFolder = ProjectDataUtils.getDomainFolder(
|
||||
mirrorRootProjectFolder, FSUtilities.mirroredProjectPath(currentParentDirPath));
|
||||
FileAttributes fattrs = fs.getFileAttributes(currentFile, monitor);
|
||||
String symlinkDest = fattrs.get(SYMLINK_DEST_ATTR, String.class, null);
|
||||
if (symlinkDest != null) {
|
||||
MirroredLink mirroredLink =
|
||||
mirrorLinkInProject(currentFile, symlinkDest, parentProjectFolder, monitor);
|
||||
String symlink = mirroredLink.relative()
|
||||
? FSUtilities.appendPath(currentParentDirPath, mirroredLink.symlink())
|
||||
: mirroredLink.symlink();
|
||||
symlink = Path.of(symlink).normalize().toString(); // fixup any '.' and '..'
|
||||
String[] oldElements = pathElements;
|
||||
String[] newElements = FSUtilities.splitPath(symlink);
|
||||
pathElements =
|
||||
Arrays.copyOf(newElements, newElements.length + oldElements.length - i - 1);
|
||||
System.arraycopy(oldElements, i + 1, pathElements, newElements.length,
|
||||
pathElements.length - newElements.length);
|
||||
i = -1;
|
||||
}
|
||||
else if (currentFile.isDirectory()) {
|
||||
ProjectDataUtils.createDomainFolderPath(mirrorRootProjectFolder,
|
||||
FSUtilities.mirroredProjectPath(currentPath));
|
||||
processedPaths.add((currentPath));
|
||||
}
|
||||
else {
|
||||
try {
|
||||
if (domainObject instanceof Program program) {
|
||||
program.withTransaction("Updating Program Info", () -> {
|
||||
program.setExecutablePath(FSUtilities.appendPath(
|
||||
parentProjectFolder.getPathname(), currentFile.getName()));
|
||||
FSRL.writeToProgramInfo(program, currentFile.getFSRL());
|
||||
});
|
||||
}
|
||||
return parentProjectFolder.createFile(currentFile.getName(), domainObject,
|
||||
monitor);
|
||||
}
|
||||
catch (DuplicateFileException e) {
|
||||
DomainFile f = parentProjectFolder.getFile(currentFile.getName());
|
||||
Msg.warn(this, "Skipping save of existing file: " + f);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IOException("Path did not point to a file!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file or folder link in the project
|
||||
*
|
||||
* @param file The {@link GFile link file}
|
||||
* @param linkDest The link destination (relative or absolute)
|
||||
* @param folder The {@link DomainFolder} to create the link in
|
||||
* @param monitor A cancelable task monitor
|
||||
* @return The newly created {@link MirroredLink project link}
|
||||
* @throws IOException if an IO-related error occurred
|
||||
*/
|
||||
private MirroredLink mirrorLinkInProject(GFile file, String linkDest, DomainFolder folder,
|
||||
TaskMonitor monitor) throws IOException {
|
||||
boolean relative = FilenameUtils.getPrefixLength(linkDest) == 0;
|
||||
String projectLinkTarget = FSUtilities.mirroredProjectPath(relative
|
||||
? FSUtilities.appendPath(projectRootPath,
|
||||
FilenameUtils.getFullPath(file.getPath()), linkDest)
|
||||
: FSUtilities.appendPath(projectRootPath, linkDest));
|
||||
DomainFile df = folder.getFile(file.getName());
|
||||
if (df == null) {
|
||||
df = folder.createLinkFile(project.getProjectData(), projectLinkTarget, relative,
|
||||
file.getName(), file.isDirectory() ? FolderLinkContentHandler.INSTANCE
|
||||
: ProgramLinkContentHandler.INSTANCE);
|
||||
}
|
||||
return new MirroredLink(df, projectLinkTarget, linkDest, relative);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.util.opinion;
|
||||
|
||||
import ghidra.formats.gfilesystem.FSRL;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -32,6 +33,7 @@ public class LoadedOpen<T extends DomainObject> extends Loaded<T> {
|
|||
*
|
||||
* @param domainObject The loaded {@link DomainObject}
|
||||
* @param domainFile The {@link DomainFile} associated with the loaded {@link DomainObject}
|
||||
* @param fsrl The {@link FSRL} of the loaded {@link DomainObject}
|
||||
* @param consumer A reference to the object "consuming" the returned {@link Loaded}
|
||||
* {@link DomainObject}, used to ensure the underlying {@link DomainObject} is only closed
|
||||
* when every consumer is done with it (see {@link #close()}). NOTE: Wrapping a
|
||||
|
@ -39,9 +41,10 @@ public class LoadedOpen<T extends DomainObject> extends Loaded<T> {
|
|||
* given {@link DomainObject} to this {@link Loaded}'s {@link #close()} method.
|
||||
* @throws LoadException if the given {@link DomainFile} is not open
|
||||
*/
|
||||
public LoadedOpen(T domainObject, DomainFile domainFile, Object consumer) throws LoadException {
|
||||
super(domainObject, domainFile.getName(), null, domainFile.getParent().getPathname(),
|
||||
consumer);
|
||||
public LoadedOpen(T domainObject, DomainFile domainFile, FSRL fsrl, Object consumer)
|
||||
throws LoadException {
|
||||
super(domainObject, domainFile.getName(), fsrl, null, domainFile.getParent().getPathname(),
|
||||
false, consumer);
|
||||
this.domainFile = domainFile;
|
||||
if (!domainFile.isOpen()) {
|
||||
throw new LoadException(domainFile + " is not open");
|
||||
|
|
|
@ -19,6 +19,8 @@ import java.io.IOException;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
|
@ -59,6 +61,52 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
|
|||
public static boolean loggingDisabled =
|
||||
SystemUtilities.getBooleanProperty("disable.loader.logging", false);
|
||||
|
||||
/**
|
||||
* A {@link Loader} configuration
|
||||
*
|
||||
* @param provider The bytes to load.
|
||||
* @param importName The name for the primary {@link Loaded} {@link DomainObject}. Path
|
||||
* information that appears at the beginning the name will be appended to the
|
||||
* {@code projectRootPath} during the {@link LoadResults#save(TaskMonitor) saving process}.
|
||||
* @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. A {@link Project} is also required during the
|
||||
* {@link LoadResults#save(TaskMonitor) saving process}. Could be {@code null} if there is no
|
||||
* project.
|
||||
* @param projectRootPath The project folder path that all {@link Loaded} {@link DomainObject}s
|
||||
* will be {@link LoadResults#save(TaskMonitor) saved} relative to. If {@code null}, "/" will
|
||||
* be used.
|
||||
* @param mirrorFsLayout True if the filesystem layout should be mirrored when
|
||||
* {@link LoadResults#save(TaskMonitor) saving}; otherwise, false
|
||||
* @param loadSpec The {@link LoadSpec} to use during load.
|
||||
* @param options The load options.
|
||||
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||
* with it (see {@link LoadResults#close()}).
|
||||
* @param log The message log.
|
||||
* @param monitor A task monitor.
|
||||
*/
|
||||
public record ImporterSettings(ByteProvider provider, String importName, Project project,
|
||||
String projectRootPath, boolean mirrorFsLayout, LoadSpec loadSpec, List<Option> options,
|
||||
Object consumer, MessageLog log, TaskMonitor monitor) {
|
||||
|
||||
/**
|
||||
* {@return The name portion of the {@code importName}, stripping off any leading path
|
||||
* information that may be present}
|
||||
*/
|
||||
public String importNameOnly() {
|
||||
return FilenameUtils.getName(importName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return The path portion of the {@code importName} if present, stripping off the
|
||||
* trailing name (could be the empty string)}
|
||||
*/
|
||||
public String importPathOnly() {
|
||||
return FilenameUtils.getFullPath(importName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -88,26 +136,7 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
|
|||
* It is also the responsibility of the caller to close the returned {@link Loaded}
|
||||
* {@link DomainObject}s with {@link LoadResults#close()} when they are no longer needed.
|
||||
*
|
||||
* @param provider The bytes to load.
|
||||
* @param loadedName A suggested name for the primary {@link Loaded} {@link DomainObject}.
|
||||
* This is just a suggestion, and a {@link Loader} implementation reserves the right to change
|
||||
* 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 options The load options.
|
||||
* @param messageLog The message log.
|
||||
* @param consumer A reference to the object "consuming" the returned {@link LoadResults}, used
|
||||
* to ensure the underlying {@link Program}s are only closed when every consumer is done
|
||||
* with it (see {@link LoadResults#close()}).
|
||||
* @param monitor A task monitor.
|
||||
* @param settings The {@link ImporterSettings}.
|
||||
* @return The {@link LoadResults} which contains one or more {@link Loaded}
|
||||
* {@link DomainObject}s (created but not saved).
|
||||
* @throws LoadException if the load failed in an expected way
|
||||
|
@ -116,27 +145,20 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
|
|||
* @throws VersionException if the load process tried to open an existing {@link DomainFile}
|
||||
* which was created with a newer or unsupported version of Ghidra
|
||||
*/
|
||||
public LoadResults<? extends DomainObject> load(ByteProvider provider, String loadedName,
|
||||
Project project, String projectFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog messageLog, Object consumer, TaskMonitor monitor) throws IOException,
|
||||
CancelledException, VersionException, LoadException;
|
||||
public LoadResults<? extends DomainObject> load(ImporterSettings settings)
|
||||
throws IOException, CancelledException, VersionException, LoadException;
|
||||
|
||||
/**
|
||||
* Loads bytes into the specified {@link Program}. This method will not create any new
|
||||
* {@link Program}s. It is only for adding to an existing {@link Program}.
|
||||
*
|
||||
* @param provider The bytes to load into the {@link Program}.
|
||||
* @param loadSpec The {@link LoadSpec} to use during load.
|
||||
* @param options The load options.
|
||||
* @param messageLog The message log.
|
||||
* @param program The {@link Program} to load into.
|
||||
* @param monitor A cancelable task monitor.
|
||||
* @param settings The {@link ImporterSettings}.
|
||||
* @throws LoadException if the load failed in an expected way.
|
||||
* @throws IOException if there was an IO-related problem loading.
|
||||
* @throws CancelledException if the user cancelled the load.
|
||||
*/
|
||||
public void loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||
public void loadInto(Program program, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException;
|
||||
|
||||
/**
|
||||
|
@ -147,10 +169,12 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
|
|||
* @param domainObject The {@link DomainObject} being loaded.
|
||||
* @param loadIntoProgram True if the load is adding to an existing {@link DomainObject};
|
||||
* otherwise, false.
|
||||
* @param mirrorFsLayout True if the filesystem layout should be mirrored when loading;
|
||||
* otherwise, false
|
||||
* @return A list of the {@link Loader}'s default options.
|
||||
*/
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram);
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout);
|
||||
|
||||
/**
|
||||
* Validates the {@link Loader}'s options and returns null if all options are valid; otherwise,
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.app.util.opinion;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import ghidra.app.util.bin.format.coff.CoffSectionHeader;
|
||||
import ghidra.app.util.bin.format.pe.SectionHeader;
|
||||
|
||||
|
@ -32,8 +34,8 @@ public class MSCoffLoader extends CoffLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCaseInsensitiveLibraryFilenames() {
|
||||
return true;
|
||||
protected Comparator<String> getLibraryNameComparator() {
|
||||
return String.CASE_INSENSITIVE_ORDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -109,11 +109,14 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
|
||||
public void load(Program program, ImporterSettings settings) throws IOException {
|
||||
|
||||
ByteProvider provider = settings.provider();
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
|
||||
if (isUniveralBinary(provider)) {
|
||||
provider = matchUniversalBinaryProvider(provider, loadSpec, monitor);
|
||||
provider = matchUniversalBinaryProvider(provider, settings.loadSpec(), monitor);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -142,9 +145,9 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
List<Option> list =
|
||||
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
List<Option> list = super.getDefaultOptions(provider, loadSpec, domainObject,
|
||||
loadIntoProgram, mirrorFsLayout);
|
||||
if (!loadIntoProgram) {
|
||||
list.add(new Option(REEXPORT_OPTION_NAME, REEXPORT_OPTION_DEFAULT,
|
||||
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-reexport"));
|
||||
|
@ -174,16 +177,16 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidSearchPath(FSRL fsrl, LoadSpec loadSpec, TaskMonitor monitor)
|
||||
protected boolean isValidSearchPath(FSRL fsrl, ImporterSettings settings)
|
||||
throws CancelledException {
|
||||
FileSystemService fsService = FileSystemService.getInstance();
|
||||
try (ByteProvider provider = fsService.getByteProvider(fsrl, loggingDisabled, monitor)) {
|
||||
try (ByteProvider provider = fsService.getByteProvider(fsrl, false, settings.monitor())) {
|
||||
if (!DyldCacheUtils.isDyldCache(provider)) {
|
||||
return true;
|
||||
}
|
||||
DyldCacheHeader header = new DyldCacheHeader(new BinaryReader(provider, true));
|
||||
DyldArchitecture dyld = header.getArchitecture();
|
||||
LanguageCompilerSpecPair lcs = loadSpec.getLanguageCompilerSpec();
|
||||
LanguageCompilerSpecPair lcs = settings.loadSpec().getLanguageCompilerSpec();
|
||||
String processor = lcs.getLanguage().getProcessor().toString().toLowerCase();
|
||||
boolean is64bit = lcs.getLanguage()
|
||||
.getAddressFactory()
|
||||
|
@ -267,10 +270,10 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected FSRL resolveLibraryFile(GFileSystem fs, String library) throws IOException {
|
||||
FSRL fsrl = super.resolveLibraryFile(fs, library);
|
||||
if (fsrl != null) {
|
||||
return fsrl;
|
||||
protected GFile lookupLibraryInFs(String library, GFileSystem fs) throws IOException {
|
||||
GFile f = super.lookupLibraryInFs(library, fs);
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
String libraryParentPath = FilenameUtils.getFullPath(library);
|
||||
String libraryName = FilenameUtils.getName(library);
|
||||
|
@ -278,13 +281,13 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
if (libraryParentDir != null) {
|
||||
for (GFile file : fs.getListing(libraryParentDir)) {
|
||||
if (file.isDirectory() && file.getName().equals("Versions")) {
|
||||
String versionsPath = joinPaths(libraryParentPath, file.getName());
|
||||
String versionsPath = FSUtilities.appendPath(libraryParentPath, file.getName());
|
||||
List<GFile> versionListion = fs.getListing(file);
|
||||
if (!versionListion.isEmpty()) {
|
||||
GFile specificVersionDir = versionListion.get(0);
|
||||
if (specificVersionDir.isDirectory()) {
|
||||
return resolveLibraryFile(fs,
|
||||
joinPaths(versionsPath, specificVersionDir.getName(), libraryName));
|
||||
return lookupLibraryInFs(FSUtilities.appendPath(versionsPath,
|
||||
specificVersionDir.getName(), libraryName), fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -292,13 +295,56 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
continue;
|
||||
}
|
||||
if (file.getName().equals(libraryName)) {
|
||||
return file.getFSRL();
|
||||
return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special Mach-O library file resolver to account for a "Versions" subdirectory being inserted
|
||||
* in the library lookup path. For example, a reference to:
|
||||
* <p>
|
||||
* {@code /System/Library/Frameworks/Foundation.framework/Foundation}
|
||||
* <p>
|
||||
* might be found at:
|
||||
* <p>
|
||||
* {@code /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation}
|
||||
* <hr>
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected DomainFile lookupLibraryInFolder(String libraryName, DomainFolder folder) {
|
||||
DomainFolder versionsFolder = folder.getFolder("Versions");
|
||||
if (versionsFolder != null) {
|
||||
DomainFolder[] versions = versionsFolder.getFolders();
|
||||
if (versions.length > 0) {
|
||||
folder = versions[0];
|
||||
}
|
||||
}
|
||||
return super.lookupLibraryInFolder(libraryName, folder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special Mach-O library {@link Comparator} to account for a "Versions" subdirectory being
|
||||
* inserted in the library lookup path. For example, a reference to:
|
||||
* <p>
|
||||
* {@code /System/Library/Frameworks/Foundation.framework/Foundation}
|
||||
* <p>
|
||||
* might be found at:
|
||||
* <p>
|
||||
* {@code /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation}
|
||||
* <hr>
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected Comparator<String> getLibraryNameComparator() {
|
||||
String versionRegex = "Versions/.+/";
|
||||
return (s1, s2) -> s1.replaceAll(versionRegex, "")
|
||||
.compareTo(s2.replaceAll(versionRegex, ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
|
@ -306,11 +352,11 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
* set and the Mach-O actually has {@code LC_REEXPORT_DYLIB} entries.
|
||||
*/
|
||||
@Override
|
||||
protected boolean shouldSearchAllPaths(Program program, List<Option> options, MessageLog log) {
|
||||
if (super.shouldSearchAllPaths(program, options, log)) {
|
||||
protected boolean shouldSearchAllPaths(Program program, ImporterSettings settings) {
|
||||
if (super.shouldSearchAllPaths(program, settings)) {
|
||||
return true;
|
||||
}
|
||||
if (shouldPerformReexports(options)) {
|
||||
if (shouldPerformReexports(settings)) {
|
||||
try {
|
||||
Symbol header =
|
||||
program.getSymbolTable().getSymbols(MachoProgramBuilder.HEADER_SYMBOL).next();
|
||||
|
@ -323,7 +369,8 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendMsg("Failed to parse Mach-O header for: '%s': %s"
|
||||
settings.log()
|
||||
.appendMsg("Failed to parse Mach-O header for: '%s': %s"
|
||||
.formatted(program.getName(), e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
@ -339,17 +386,16 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
* depth would have prevented their save as a normal library)
|
||||
*/
|
||||
@Override
|
||||
protected void processLibrary(Program lib, String libName, FSRL libFsrl, ByteProvider provider,
|
||||
Queue<UnprocessedLibrary> unprocessed, int depth, LoadSpec loadSpec,
|
||||
List<Option> options, MessageLog log, TaskMonitor monitor)
|
||||
protected void processLibrary(Program lib, String libName, FSRL libFsrl,
|
||||
Queue<UnprocessedLibrary> unprocessed, int depth, ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
if (!shouldPerformReexports(options)) {
|
||||
if (!shouldPerformReexports(settings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
for (String path : getReexportPaths(lib, log)) {
|
||||
for (String path : getReexportPaths(lib, settings.log())) {
|
||||
unprocessed.add(new UnprocessedLibrary(path, depth, depth == 1));
|
||||
}
|
||||
}
|
||||
|
@ -364,19 +410,20 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
* Adds reexported symbols to each {@link Loaded} {@link Program}.
|
||||
*/
|
||||
@Override
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||
LoadSpec loadSpec, List<Option> options, MessageLog log, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms,
|
||||
ImporterSettings settings) throws CancelledException, IOException {
|
||||
|
||||
if (shouldPerformReexports(options)) {
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
|
||||
List<DomainFolder> searchFolders =
|
||||
getLibrarySearchFolders(loadedPrograms, project, options, log);
|
||||
if (shouldPerformReexports(settings)) {
|
||||
|
||||
List<DomainFolder> searchFolders = getLibrarySearchFolders(loadedPrograms, settings);
|
||||
|
||||
Program firstProgram = loadedPrograms.getFirst().getDomainObject(this);
|
||||
List<LibrarySearchPath> searchPaths;
|
||||
try {
|
||||
searchPaths = getLibrarySearchPaths(firstProgram, loadSpec, options, log, monitor);
|
||||
searchPaths = getLibrarySearchPaths(firstProgram, settings);
|
||||
}
|
||||
finally {
|
||||
firstProgram.release(this);
|
||||
|
@ -389,8 +436,7 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
Program program = loadedProgram.getDomainObject(this);
|
||||
int id = program.startTransaction("Reexporting");
|
||||
try {
|
||||
reexport(program, loadedPrograms, searchFolders, searchPaths, options, monitor,
|
||||
log);
|
||||
reexport(program, loadedPrograms, searchFolders, searchPaths, settings);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendException(e);
|
||||
|
@ -402,8 +448,7 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
}
|
||||
}
|
||||
|
||||
super.postLoadProgramFixups(loadedPrograms, project, loadSpec, options, log,
|
||||
monitor);
|
||||
super.postLoadProgramFixups(loadedPrograms, settings);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -507,11 +552,12 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
/**
|
||||
* Checks to see if reexports should be performed
|
||||
*
|
||||
* @param options a {@link List} of {@link Option}s
|
||||
* @param settings The {@link Loader.ImporterSettings}
|
||||
* @return True if reexports should be performed; otherwise, false
|
||||
*/
|
||||
private boolean shouldPerformReexports(List<Option> options) {
|
||||
return OptionUtils.getOption(REEXPORT_OPTION_NAME, options, REEXPORT_OPTION_DEFAULT);
|
||||
private boolean shouldPerformReexports(ImporterSettings settings) {
|
||||
return OptionUtils.getOption(REEXPORT_OPTION_NAME, settings.options(),
|
||||
REEXPORT_OPTION_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -550,16 +596,16 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
* @param searchFolders A {@link List} of project folders that may contain already-loaded
|
||||
* {@link Program}s with reexportable symbols
|
||||
* @param searchPaths A {@link List} of file system search paths that will be searched
|
||||
* @param options The load options
|
||||
* @param monitor A cancelable task monitor
|
||||
* @param log The log
|
||||
* @param settings The {@link Loader.ImporterSettings}
|
||||
* @throws CancelledException if the user cancelled the load operation
|
||||
* @throws IOException if there was an IO-related error during the load
|
||||
*/
|
||||
private void reexport(Program program, List<Loaded<Program>> loadedPrograms,
|
||||
List<DomainFolder> searchFolders, List<LibrarySearchPath> searchPaths,
|
||||
List<Option> options, TaskMonitor monitor, MessageLog log)
|
||||
ImporterSettings settings)
|
||||
throws CancelledException, Exception {
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
|
||||
for (String path : getReexportPaths(program, log)) {
|
||||
monitor.checkCancelled();
|
||||
|
@ -572,7 +618,7 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
if (lib == null) {
|
||||
for (DomainFolder searchFolder : searchFolders) {
|
||||
DomainFile df =
|
||||
findLibraryInProject(path, searchFolder, searchPaths, options, monitor);
|
||||
findLibraryInProject(path, searchFolder, searchPaths, settings);
|
||||
if (df != null) {
|
||||
DomainObject obj = df.getDomainObject(this, true, true, monitor);
|
||||
if (obj instanceof Program p) {
|
||||
|
|
|
@ -19,7 +19,6 @@ import java.io.*;
|
|||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
@ -92,8 +91,11 @@ public class MapLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program prog,
|
||||
TaskMonitor monitor, MessageLog log) throws IOException, CancelledException {
|
||||
public void load(Program prog, ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
|
||||
if (!prog.getExecutableFormat().equals(PeLoader.PE_NAME)) {
|
||||
throw new IOException("Program must be a " + PeLoader.PE_NAME);
|
||||
|
@ -103,7 +105,7 @@ public class MapLoader extends AbstractProgramWrapperLoader {
|
|||
AddressSpace space = prog.getAddressFactory().getDefaultAddressSpace();
|
||||
int successCount = 0;
|
||||
|
||||
List<MapSymbol> symbols = parseMapFile(provider, monitor, log);
|
||||
List<MapSymbol> symbols = parseMapFile(settings.provider(), monitor, log);
|
||||
monitor.initialize(symbols.size(), "Creating symbols...");
|
||||
for (MapSymbol symbol : symbols) {
|
||||
monitor.increment();
|
||||
|
|
|
@ -23,7 +23,6 @@ import ghidra.app.util.Option;
|
|||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -160,23 +159,15 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||
protected List<Loaded<Program>> loadProgram(ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||
CompilerSpec importerCompilerSpec =
|
||||
importerLanguage.getCompilerSpecByID(pair.compilerSpecID);
|
||||
|
||||
Program prog = createProgram(provider, programName, null, getName(), importerLanguage,
|
||||
importerCompilerSpec, consumer);
|
||||
List<Loaded<Program>> loadedList =
|
||||
List.of(new Loaded<>(prog, programName, project, programFolderPath, consumer));
|
||||
Program prog = createProgram(null, settings);
|
||||
List<Loaded<Program>> loadedList = List.of(new Loaded<>(prog, settings));
|
||||
boolean success = false;
|
||||
try {
|
||||
loadInto(provider, loadSpec, options, log, prog, monitor);
|
||||
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
||||
loadInto(prog, settings);
|
||||
createDefaultMemoryBlocks(prog, settings);
|
||||
success = true;
|
||||
return loadedList;
|
||||
}
|
||||
|
@ -188,16 +179,16 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
||||
protected void loadProgramInto(Program prog, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
Address baseAddr = getBaseAddr(options);
|
||||
Address baseAddr = getBaseAddr(settings.options());
|
||||
|
||||
if (baseAddr == null) {
|
||||
baseAddr = prog.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
}
|
||||
try {
|
||||
processMotorolaHex(provider, options, prog, baseAddr, monitor);
|
||||
processMotorolaHex(settings.provider(), settings.options(), prog, baseAddr,
|
||||
settings.monitor());
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new LoadException(
|
||||
|
@ -426,7 +417,7 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
String blockName = "";
|
||||
boolean isOverlay = false;
|
||||
Address baseAddr = null;
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.math.BigInteger;
|
|||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.mz.*;
|
||||
|
@ -81,18 +80,21 @@ public class MzLoader extends AbstractLibrarySupportLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
public void load(Program program, ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
|
||||
FileBytes fileBytes =
|
||||
MemoryBlockUtils.createFileBytes(program, settings.provider(), monitor);
|
||||
AddressFactory af = program.getAddressFactory();
|
||||
if (!(af.getDefaultAddressSpace() instanceof SegmentedAddressSpace)) {
|
||||
throw new IOException("Selected Language must have a segmented address space.");
|
||||
}
|
||||
|
||||
SegmentedAddressSpace space = (SegmentedAddressSpace) af.getDefaultAddressSpace();
|
||||
MzExecutable mz = new MzExecutable(provider);
|
||||
MzExecutable mz = new MzExecutable(settings.provider());
|
||||
|
||||
try {
|
||||
Set<RelocationFixup> relocationFixups = getRelocationFixups(space, mz, log, monitor);
|
||||
|
|
|
@ -20,8 +20,9 @@ import java.io.IOException;
|
|||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.ne.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
|
@ -84,8 +85,11 @@ public class NeLoader extends AbstractOrdinalSupportLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program prog,
|
||||
TaskMonitor monitor, MessageLog log) throws IOException, CancelledException {
|
||||
public void load(Program prog, ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
|
||||
if (monitor.isCancelled()) {
|
||||
return;
|
||||
|
@ -94,10 +98,11 @@ public class NeLoader extends AbstractOrdinalSupportLoader {
|
|||
|
||||
initVars();
|
||||
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(prog, provider, monitor);
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(prog, settings.provider(), monitor);
|
||||
SegmentedAddressSpace space =
|
||||
(SegmentedAddressSpace) prog.getAddressFactory().getDefaultAddressSpace();
|
||||
NewExecutable ne = new NewExecutable(provider, space.getAddress(SEGMENT_START, 0));
|
||||
NewExecutable ne =
|
||||
new NewExecutable(settings.provider(), space.getAddress(SEGMENT_START, 0));
|
||||
WindowsHeader wh = ne.getWindowsHeader();
|
||||
InformationBlock ib = wh.getInformationBlock();
|
||||
SegmentTable st = wh.getSegmentTable();
|
||||
|
@ -171,13 +176,9 @@ public class NeLoader extends AbstractOrdinalSupportLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean isOptionalLibraryFilenameExtensions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCaseInsensitiveLibraryFilenames() {
|
||||
return true;
|
||||
protected Comparator<String> getLibraryNameComparator() {
|
||||
return (s1, s2) -> String.CASE_INSENSITIVE_ORDER.compare(FilenameUtils.getBaseName(s1),
|
||||
FilenameUtils.getBaseName(s2));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.util.*;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.omf.*;
|
||||
|
@ -74,12 +73,13 @@ public class Omf51Loader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
protected void load(Program program, ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
AbstractOmfRecordFactory factory = new Omf51RecordFactory(provider);
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
FileBytes fileBytes =
|
||||
MemoryBlockUtils.createFileBytes(program, settings.provider(), monitor);
|
||||
AbstractOmfRecordFactory factory = new Omf51RecordFactory(settings.provider());
|
||||
try {
|
||||
List<OmfRecord> records = OmfUtils.readRecords(factory);
|
||||
Map<Integer, Address> segmentToAddr =
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.util.*;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.omf.*;
|
||||
|
@ -109,12 +108,12 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
protected void load(Program program, ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
OmfFileHeader header = null;
|
||||
AbstractOmfRecordFactory factory = new OmfRecordFactory(provider);
|
||||
AbstractOmfRecordFactory factory = new OmfRecordFactory(settings.provider());
|
||||
try {
|
||||
header = OmfFileHeader.parse(factory, monitor, log);
|
||||
header.resolveNames();
|
||||
|
@ -125,13 +124,15 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
|
|||
if (header == null) {
|
||||
throw new IOException("OMF File header was corrupted. " + e.getMessage());
|
||||
}
|
||||
log.appendMsg("File was corrupted - leaving partial program " + provider.getName());
|
||||
log.appendMsg(
|
||||
"File was corrupted - leaving partial program " + settings.provider().getName());
|
||||
}
|
||||
|
||||
// We don't use the file bytes to create block because the bytes are manipulated before
|
||||
// forming the block. Creating the FileBytes anyway in case later we want access to all
|
||||
// the original bytes.
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
FileBytes fileBytes =
|
||||
MemoryBlockUtils.createFileBytes(program, settings.provider(), monitor);
|
||||
|
||||
try {
|
||||
processSegmentHeaders(factory.getReader(), header, program, monitor, log);
|
||||
|
|
|
@ -19,6 +19,8 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
import ghidra.app.plugin.core.analysis.rust.RustConstants;
|
||||
|
@ -103,16 +105,18 @@ public class PeLoader extends AbstractPeDebugLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
protected void load(Program program, ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
|
||||
if (monitor.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PortableExecutable pe = new PortableExecutable(provider, getSectionLayout(), false,
|
||||
shouldParseCliHeaders(options));
|
||||
PortableExecutable pe = new PortableExecutable(settings.provider(), getSectionLayout(),
|
||||
false, shouldParseCliHeaders(settings.options()));
|
||||
|
||||
NTHeader ntHeader = pe.getNTHeader();
|
||||
if (ntHeader == null) {
|
||||
|
@ -121,7 +125,7 @@ public class PeLoader extends AbstractPeDebugLoader {
|
|||
OptionalHeader optionalHeader = ntHeader.getOptionalHeader();
|
||||
|
||||
monitor.setMessage("Completing PE header parsing...");
|
||||
FileBytes fileBytes = createFileBytes(provider, program, monitor);
|
||||
FileBytes fileBytes = createFileBytes(settings.provider(), program, monitor);
|
||||
try {
|
||||
Map<SectionHeader, Address> sectionToAddress =
|
||||
processMemoryBlocks(pe, program, fileBytes, monitor, log);
|
||||
|
@ -146,14 +150,16 @@ public class PeLoader extends AbstractPeDebugLoader {
|
|||
processImports(optionalHeader, program, monitor, log);
|
||||
processDelayImports(optionalHeader, program, monitor, log);
|
||||
processRelocations(optionalHeader, program, monitor, log);
|
||||
processDebug(optionalHeader, ntHeader, sectionToAddress, program, options, monitor);
|
||||
processDebug(optionalHeader, ntHeader, sectionToAddress, program, settings.options(),
|
||||
monitor);
|
||||
processProperties(optionalHeader, ntHeader, program, monitor);
|
||||
processComments(program.getListing(), monitor);
|
||||
processSymbols(ntHeader, sectionToAddress, program, monitor, log);
|
||||
|
||||
processEntryPoints(ntHeader, program, monitor);
|
||||
String compiler =
|
||||
CompilerOpinion.getOpinion(pe, provider, program, monitor, log).toString();
|
||||
CompilerOpinion.getOpinion(pe, settings.provider(), program, monitor, log)
|
||||
.toString();
|
||||
program.setCompiler(compiler);
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
|
@ -183,9 +189,9 @@ public class PeLoader extends AbstractPeDebugLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
List<Option> list =
|
||||
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
List<Option> list = super.getDefaultOptions(provider, loadSpec, domainObject,
|
||||
loadIntoProgram, mirrorFsLayout);
|
||||
if (!loadIntoProgram) {
|
||||
list.add(new Option(PARSE_CLI_HEADERS_OPTION_NAME, PARSE_CLI_HEADERS_OPTION_DEFAULT,
|
||||
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-parseCliHeaders"));
|
||||
|
@ -210,8 +216,9 @@ public class PeLoader extends AbstractPeDebugLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCaseInsensitiveLibraryFilenames() {
|
||||
return true;
|
||||
protected Comparator<String> getLibraryNameComparator() {
|
||||
return (s1, s2) -> String.CASE_INSENSITIVE_ORDER.compare(FilenameUtils.getName(s1),
|
||||
FilenameUtils.getName(s2));
|
||||
}
|
||||
|
||||
private boolean shouldParseCliHeaders(List<Option> options) {
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.*;
|
|||
import ghidra.app.cmd.data.CreateDataCmd;
|
||||
import ghidra.app.cmd.label.AddUniqueLabelCmd;
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.pef.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
|
@ -68,15 +67,17 @@ public class PefLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
public void load(Program program, ImporterSettings settings)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
FileBytes fileBytes =
|
||||
MemoryBlockUtils.createFileBytes(program, settings.provider(), monitor);
|
||||
|
||||
ImportStateCache importState = null;
|
||||
try {
|
||||
ContainerHeader header = new ContainerHeader(provider);
|
||||
ContainerHeader header = new ContainerHeader(settings.provider());
|
||||
monitor.setMessage("Completing PEF header parsing...");
|
||||
monitor.setCancelEnabled(false);
|
||||
header.parse();
|
||||
|
|
|
@ -22,13 +22,11 @@ import ghidra.app.util.Option;
|
|||
import ghidra.app.util.OptionException;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.unixaout.UnixAoutHeader;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.LanguageCompilerSpecPair;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A {@link Loader} for processing UNIX-style A.out executables
|
||||
|
@ -73,15 +71,14 @@ public class UnixAoutLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
protected void load(Program program, ImporterSettings settings)
|
||||
throws CancelledException, IOException {
|
||||
final boolean isLittleEndian = !program.getLanguage().isBigEndian();
|
||||
final UnixAoutHeader header = new UnixAoutHeader(provider, isLittleEndian);
|
||||
final UnixAoutHeader header = new UnixAoutHeader(settings.provider(), isLittleEndian);
|
||||
|
||||
final UnixAoutProgramLoader loader =
|
||||
new UnixAoutProgramLoader(program, header, monitor, log);
|
||||
loader.loadAout(getBaseAddrOffset(options));
|
||||
new UnixAoutProgramLoader(program, header, settings.monitor(), settings.log());
|
||||
loader.loadAout(getBaseAddrOffset(settings.options()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -112,7 +109,7 @@ public class UnixAoutLoader extends AbstractProgramWrapperLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
Address baseAddr = null;
|
||||
|
||||
if (domainObject instanceof Program) {
|
||||
|
@ -130,7 +127,8 @@ public class UnixAoutLoader extends AbstractProgramWrapperLoader {
|
|||
list.add(new Option(OPTION_NAME_BASE_ADDR, baseAddr, Address.class,
|
||||
Loader.COMMAND_LINE_ARG_PREFIX + "-baseAddr"));
|
||||
|
||||
list.addAll(super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram));
|
||||
list.addAll(super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram,
|
||||
mirrorFsLayout));
|
||||
return list;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ import ghidra.app.util.bin.ByteProvider;
|
|||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.xml.*;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -177,18 +176,14 @@ public class XmlLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||
protected List<Loaded<Program>> loadProgram(ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
List<Loaded<Program>> results = new ArrayList<>();
|
||||
|
||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||
LanguageCompilerSpecPair pair = settings.loadSpec().getLanguageCompilerSpec();
|
||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||
CompilerSpec importerCompilerSpec =
|
||||
importerLanguage.getCompilerSpecByID(pair.compilerSpecID);
|
||||
|
||||
ParseResult result = parse(provider);
|
||||
ParseResult result = parse(settings.provider());
|
||||
|
||||
if (result.lastInfo == null) {
|
||||
return results;
|
||||
|
@ -197,15 +192,14 @@ public class XmlLoader extends AbstractProgramLoader {
|
|||
if (result.lastInfo.imageBase != null) {
|
||||
imageBase = importerLanguage.getAddressFactory().getAddress(result.lastInfo.imageBase);
|
||||
}
|
||||
Program prog = createProgram(provider, programName, imageBase, getName(), importerLanguage,
|
||||
importerCompilerSpec, consumer);
|
||||
List<Loaded<Program>> loadedList =
|
||||
List.of(new Loaded<>(prog, programName, project, programFolderPath, consumer));
|
||||
Program prog = createProgram(imageBase, settings);
|
||||
List<Loaded<Program>> loadedList = List.of(new Loaded<>(prog, settings));
|
||||
boolean success = false;
|
||||
try {
|
||||
success = doImport(result.lastXmlMgr, options, log, prog, monitor, false);
|
||||
success = doImport(result.lastXmlMgr, settings.options(), settings.log(), prog,
|
||||
settings.monitor(), false);
|
||||
if (success) {
|
||||
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
||||
createDefaultMemoryBlocks(prog, settings);
|
||||
return loadedList;
|
||||
}
|
||||
throw new LoadException("Failed to load");
|
||||
|
@ -218,11 +212,11 @@ public class XmlLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
||||
protected void loadProgramInto(Program prog, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
File file = provider.getFile();
|
||||
doImport(new ProgramXmlMgr(file), options, log, prog, monitor, true);
|
||||
File file = settings.provider().getFile();
|
||||
doImport(new ProgramXmlMgr(file), settings.options(), settings.log(), prog,
|
||||
settings.monitor(), true);
|
||||
}
|
||||
|
||||
private boolean doImportWork(final ProgramXmlMgr mgr, final List<Option> options,
|
||||
|
@ -325,7 +319,7 @@ public class XmlLoader extends AbstractProgramLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
return new XmlProgramOptions().getOptions(loadIntoProgram);
|
||||
}
|
||||
|
||||
|
@ -335,7 +329,8 @@ public class XmlLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
|
||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program) {
|
||||
// XXX will this work? is there other state that xmlOptions needs to
|
||||
// know?
|
||||
try {
|
||||
|
|
|
@ -651,4 +651,34 @@ public class FSUtilities {
|
|||
return FileType.UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the given path into its individual directory and filename components. For example,
|
||||
* {@code "/dir/dir/dir/file"} becomes {@code ["", "dir", "dir", "dir", "file"]}.
|
||||
*
|
||||
* @param path The path to split
|
||||
* @return The split path
|
||||
*/
|
||||
public static String[] splitPath(String path) {
|
||||
return Objects.requireNonNullElse(path, "").replace('\\', '/').split("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given path to a valid mirrored project path. Care must be taken to minimize
|
||||
* project path collisions, allowing them only when completely necessary.
|
||||
*
|
||||
* @param path The path to convert
|
||||
* @return The mirrored project path
|
||||
*/
|
||||
public static String mirroredProjectPath(String path) {
|
||||
path = FSUtilities.normalizeNativePath(path);
|
||||
|
||||
// If it looks like an absolute Windows path, drop the colon from the drive letter. The
|
||||
// colon is not a valid project character.
|
||||
if (path.length() >= 3 && path.charAt(0) == '/' && Character.isLetter(path.charAt(1)) &&
|
||||
path.charAt(2) == ':') {
|
||||
path = "/" + path.charAt(1) + path.substring(3);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -179,7 +179,7 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
|||
try {
|
||||
FileData<METADATATYPE> baseDirData = getFileData(baseDir);
|
||||
FileData<METADATATYPE> fileData =
|
||||
lookup(baseDirData, splitPath(path), -1, false, nameComp);
|
||||
lookup(baseDirData, FSUtilities.splitPath(path), -1, false, nameComp);
|
||||
return (fileData != null) ? fileData.file : null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
@ -222,7 +222,7 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
|||
|
||||
symlinkPathDebug.append("[");
|
||||
FileData<METADATATYPE> currentFile = Objects.requireNonNullElse(baseDir, rootDir);
|
||||
String[] pathparts = splitPath(path);
|
||||
String[] pathparts = FSUtilities.splitPath(path);
|
||||
for (int i = 0; i < pathparts.length && currentFile != null; i++) {
|
||||
String name = pathparts[i];
|
||||
symlinkPathDebug.append(i != 0 ? "," : "").append(name);
|
||||
|
@ -309,7 +309,7 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
|||
public synchronized GFile storeFile(String path, long fileIndex, boolean isDirectory,
|
||||
long length, METADATATYPE metadata) {
|
||||
|
||||
String[] nameparts = splitPath(path);
|
||||
String[] nameparts = FSUtilities.splitPath(path);
|
||||
if (nameparts.length == 0) {
|
||||
return rootDir.file;
|
||||
}
|
||||
|
@ -369,7 +369,7 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
|||
*/
|
||||
public synchronized GFile storeSymlink(String path, long fileIndex, String symlinkPath,
|
||||
long length, METADATATYPE metadata) {
|
||||
String[] nameparts = splitPath(path);
|
||||
String[] nameparts = FSUtilities.splitPath(path);
|
||||
if (nameparts.length == 0) {
|
||||
Msg.warn(this,
|
||||
"Unable to create invalid symlink file [%s] -> [%s]".formatted(path, symlinkPath));
|
||||
|
@ -498,10 +498,6 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
|||
return parent.file;
|
||||
}
|
||||
|
||||
protected String[] splitPath(String path) {
|
||||
return Objects.requireNonNullElse(path, "").replace('\\', '/').split("/");
|
||||
}
|
||||
|
||||
protected FileData<METADATATYPE> lookupFileInDir(
|
||||
Map<String, FileData<METADATATYPE>> dirContents, String filename,
|
||||
Comparator<String> nameComp) {
|
||||
|
|
|
@ -80,4 +80,9 @@ public class FileSystemRef implements Closeable {
|
|||
Msg.warn(this, "Unclosed FilesytemRef: " + fs.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return fs.getFSRL().toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ public class AddToProgramDialog extends ImporterDialog {
|
|||
folderButton.setEnabled(false);
|
||||
languageButton.setEnabled(false);
|
||||
nameTextField.setEnabled(false);
|
||||
mirrorFsCheckBox.setVisible(false);
|
||||
validateFormInput();
|
||||
}
|
||||
|
||||
|
@ -78,8 +79,8 @@ public class AddToProgramDialog extends ImporterDialog {
|
|||
|
||||
LoadSpec loadSpec = getSelectedLoadSpec(loader);
|
||||
|
||||
String result =
|
||||
loader.validateOptions(byteProvider, loadSpec, getOptions(loadSpec), addToProgram);
|
||||
String result = loader.validateOptions(byteProvider, loadSpec, getOptions(loadSpec, false),
|
||||
addToProgram);
|
||||
|
||||
if (result != null) {
|
||||
setStatusText(result);
|
||||
|
@ -103,7 +104,8 @@ public class AddToProgramDialog extends ImporterDialog {
|
|||
LoadSpec selectedLoadSpec = getSelectedLoadSpec(selectedLoader);
|
||||
|
||||
if (options == null) {
|
||||
options = selectedLoader.getDefaultOptions(byteProvider, selectedLoadSpec, null, true);
|
||||
options =
|
||||
selectedLoader.getDefaultOptions(byteProvider, selectedLoadSpec, null, true, false);
|
||||
}
|
||||
TaskLauncher.launchNonModal("Import File", monitor -> {
|
||||
ImporterUtilities.addContentToProgram(tool, addToProgram, fsrl, selectedLoadSpec,
|
||||
|
@ -113,11 +115,12 @@ public class AddToProgramDialog extends ImporterDialog {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected List<Option> getOptions(LoadSpec loadSpec) {
|
||||
if (options != null) {
|
||||
protected List<Option> getOptions(LoadSpec loadSpec, boolean forceRefresh) {
|
||||
if (options != null && !forceRefresh) {
|
||||
return options;
|
||||
}
|
||||
return loadSpec.getLoader().getDefaultOptions(byteProvider, loadSpec, addToProgram, true);
|
||||
return loadSpec.getLoader()
|
||||
.getDefaultOptions(byteProvider, loadSpec, addToProgram, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -55,9 +55,9 @@ import ghidra.framework.store.local.LocalFileSystem;
|
|||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.lang.LanguageCompilerSpecPair;
|
||||
import ghidra.program.model.lang.LanguageNotFoundException;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
import ghidra.util.layout.VerticalLayout;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.layout.*;
|
||||
import ghidra.util.task.TaskBuilder;
|
||||
|
||||
/**
|
||||
|
@ -74,6 +74,7 @@ public class ImporterDialog extends DialogComponentProvider {
|
|||
private DomainFolder destinationFolder;
|
||||
private boolean languageNeeded;
|
||||
private String suggestedDestinationPath;
|
||||
private String previousName;
|
||||
|
||||
protected ByteProvider byteProvider;
|
||||
protected JTextField nameTextField;
|
||||
|
@ -81,6 +82,7 @@ public class ImporterDialog extends DialogComponentProvider {
|
|||
protected JButton folderButton;
|
||||
protected JButton languageButton;
|
||||
protected JTextField languageTextField;
|
||||
protected JCheckBox mirrorFsCheckBox;
|
||||
protected JButton optionsButton;
|
||||
protected JTextField folderNameTextField;
|
||||
protected GhidraComboBox<Loader> loaderComboBox;
|
||||
|
@ -322,13 +324,21 @@ public class ImporterDialog extends DialogComponentProvider {
|
|||
|
||||
private Component buildButtonPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
JPanel innerPanel = new JPanel(new VerticalLayout(5));
|
||||
JPanel innerPanel = new JPanel(new HorizontalLayout(5));
|
||||
innerPanel.add(buildMirrorFsCheckbox());
|
||||
innerPanel.add(buildOptionsButton());
|
||||
panel.add(innerPanel, BorderLayout.EAST);
|
||||
panel.getAccessibleContext().setAccessibleName("Buttons");
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildMirrorFsCheckbox() {
|
||||
mirrorFsCheckBox = new JCheckBox("Mirror Filesystem", false);
|
||||
mirrorFsCheckBox.addActionListener(e -> mirrorFs());
|
||||
mirrorFsCheckBox.getAccessibleContext().setAccessibleName("Mirror");
|
||||
return mirrorFsCheckBox;
|
||||
}
|
||||
|
||||
private Component buildOptionsButton() {
|
||||
optionsButton = new JButton("Options...");
|
||||
optionsButton.addActionListener(e -> showOptions());
|
||||
|
@ -338,18 +348,19 @@ public class ImporterDialog extends DialogComponentProvider {
|
|||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
if (validateFormInput()) {
|
||||
if (!validateFormInput()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Loader loader = getSelectedLoader();
|
||||
LoadSpec loadSpec = getSelectedLoadSpec(loader);
|
||||
String programPath = removeTrailingSlashes(getName());
|
||||
DomainFolder importFolder = getOrCreateImportFolder(destinationFolder, programPath);
|
||||
String programName = FilenameUtils.getName(programPath);
|
||||
options = getOptions(loadSpec); // make sure you get the options now, before the ByteProvider is closed.
|
||||
options = getOptions(loadSpec, false); // make sure you get the options now, before the ByteProvider is closed.
|
||||
|
||||
//@formatter:off
|
||||
new TaskBuilder("Import File", monitor -> {
|
||||
ImporterUtilities.importSingleFile(tool, programManager, fsrl, importFolder,
|
||||
loadSpec, programName, options, monitor);
|
||||
ImporterUtilities.importSingleFile(tool, programManager, fsrl,
|
||||
destinationFolder.getPathname(), mirrorFsCheckBox.isSelected(), loadSpec,
|
||||
removeTrailingSlashes(getName()), options, monitor);
|
||||
})
|
||||
.setLaunchDelay(0)
|
||||
.launchNonModal();
|
||||
|
@ -357,7 +368,6 @@ public class ImporterDialog extends DialogComponentProvider {
|
|||
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
private String removeTrailingSlashes(String path) {
|
||||
while (path.endsWith("/")) {
|
||||
|
@ -366,24 +376,6 @@ public class ImporterDialog extends DialogComponentProvider {
|
|||
return path;
|
||||
}
|
||||
|
||||
private DomainFolder getOrCreateImportFolder(DomainFolder parentFolder, String programPath) {
|
||||
int lastIndexOf = programPath.lastIndexOf("/");
|
||||
if (lastIndexOf < 0) {
|
||||
return parentFolder;
|
||||
}
|
||||
String folderPath = programPath.substring(0, lastIndexOf);
|
||||
try {
|
||||
return ProjectDataUtils.createDomainFolderPath(parentFolder, folderPath);
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
Msg.showError(this, null, "Error Creating Folders", e.getMessage());
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "Error Creating Folders", "I/O Error" + e.getMessage(), e);
|
||||
}
|
||||
return parentFolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
|
@ -395,13 +387,29 @@ public class ImporterDialog extends DialogComponentProvider {
|
|||
}
|
||||
}
|
||||
|
||||
protected List<Option> getOptions(LoadSpec loadSpec) {
|
||||
if (options == null) {
|
||||
options = loadSpec.getLoader().getDefaultOptions(byteProvider, loadSpec, null, false);
|
||||
protected List<Option> getOptions(LoadSpec loadSpec, boolean forceRefresh) {
|
||||
if (options == null || forceRefresh) {
|
||||
options = loadSpec.getLoader()
|
||||
.getDefaultOptions(byteProvider, loadSpec, null, false,
|
||||
mirrorFsCheckBox.isSelected());
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
private void mirrorFs() {
|
||||
nameTextField.setEnabled(!mirrorFsCheckBox.isSelected());
|
||||
if (mirrorFsCheckBox.isSelected()) {
|
||||
previousName = getName();
|
||||
String path = FSUtilities.mirroredProjectPath(fsrl.getPath());
|
||||
nameTextField.setText(path);
|
||||
nameTextField.setCaretPosition(path.length());
|
||||
}
|
||||
else if (previousName != null) {
|
||||
nameTextField.setText(previousName);
|
||||
nameTextField.setCaretPosition(previousName.length());
|
||||
}
|
||||
}
|
||||
|
||||
private void showOptions() {
|
||||
try {
|
||||
Loader loader = getSelectedLoader();
|
||||
|
@ -412,7 +420,7 @@ public class ImporterDialog extends DialogComponentProvider {
|
|||
|
||||
AddressFactoryService service = () -> addressFactory;
|
||||
|
||||
List<Option> currentOptions = getOptions(loadSpec);
|
||||
List<Option> currentOptions = getOptions(loadSpec, true);
|
||||
if (currentOptions.isEmpty()) {
|
||||
Msg.showInfo(this, null, "Options", "There are no options for this importer!");
|
||||
return;
|
||||
|
|
|
@ -42,7 +42,8 @@ import ghidra.formats.gfilesystem.FileCache.FileCacheEntry;
|
|||
import ghidra.formats.gfilesystem.FileCache.FileCacheEntryBuilder;
|
||||
import ghidra.formats.gfilesystem.FileSystemService;
|
||||
import ghidra.framework.main.*;
|
||||
import ghidra.framework.main.datatree.*;
|
||||
import ghidra.framework.main.datatree.DataTree;
|
||||
import ghidra.framework.main.datatree.JavaFileListHandler;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.*;
|
||||
|
@ -169,7 +170,7 @@ public class ImporterPlugin extends Plugin
|
|||
return false;
|
||||
}
|
||||
return loadSpec.getLoader()
|
||||
.getDefaultOptions(provider, loadSpec, null, false)
|
||||
.getDefaultOptions(provider, loadSpec, null, false, false)
|
||||
.stream()
|
||||
.anyMatch(e -> e.getName()
|
||||
.equals(AbstractLibrarySupportLoader.LOAD_ONLY_LIBRARIES_OPTION_NAME));
|
||||
|
|
|
@ -28,6 +28,7 @@ import ghidra.app.util.bin.ByteProvider;
|
|||
import ghidra.app.util.bin.FileBytesProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.app.util.opinion.Loader.ImporterSettings;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.framework.main.AppInfo;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
|
@ -276,7 +277,7 @@ public class ImporterUtilities {
|
|||
}
|
||||
LoadSpec loadSpec = getLoadSpec(provider, program);
|
||||
if (loadSpec == null || loadSpec.getLoader()
|
||||
.getDefaultOptions(provider, loadSpec, null, false)
|
||||
.getDefaultOptions(provider, loadSpec, null, false, false)
|
||||
.stream()
|
||||
.noneMatch(e -> e.getName()
|
||||
.equals(
|
||||
|
@ -354,9 +355,10 @@ public class ImporterUtilities {
|
|||
if (program == null) {
|
||||
return;
|
||||
}
|
||||
try (LoadResults<? extends DomainObject> loadResults = new LoadResults<>(program,
|
||||
program.getName(), tool.getProject(), destFolder.getPathname(), consumer)) {
|
||||
doPostImportProcessing(tool, programManager, fsrl, loadResults, "", monitor);
|
||||
try (LoadResults<? extends DomainObject> loadResults =
|
||||
new LoadResults<>(new Loaded<>(program, program.getName(), fsrl,
|
||||
tool.getProject(), destFolder.getPathname(), false, consumer))) {
|
||||
doPostImportProcessing(tool, programManager, loadResults, "", monitor);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
@ -402,28 +404,34 @@ public class ImporterUtilities {
|
|||
* @param tool tool to which popup dialogs should be associated
|
||||
* @param programManager program manager to open imported file with or null
|
||||
* @param fsrl import file location
|
||||
* @param destFolder project destination folder
|
||||
* @param projectRootPath The project folder path that all imported things will be saved
|
||||
* relative to. If {@code null}, "/" will be used.
|
||||
* @param mirrorFsLayout True if the filesystem layout should be mirrored when loading;
|
||||
* otherwise, false
|
||||
* @param loadSpec import {@link LoadSpec}
|
||||
* @param programName program name
|
||||
* @param importName The import name. Path information that appears at the beginning the name
|
||||
* will be appended to the {@code projectRootPath} during the saving process.
|
||||
* @param options import options
|
||||
* @param monitor task monitor
|
||||
*/
|
||||
public static void importSingleFile(PluginTool tool, ProgramManager programManager, FSRL fsrl,
|
||||
DomainFolder destFolder, LoadSpec loadSpec, String programName, List<Option> options,
|
||||
TaskMonitor monitor) {
|
||||
String projectRootPath, boolean mirrorFsLayout, LoadSpec loadSpec, String importName,
|
||||
List<Option> options, TaskMonitor monitor) {
|
||||
|
||||
Objects.requireNonNull(monitor);
|
||||
|
||||
try (ByteProvider bp = fsService.getByteProvider(fsrl, false, monitor)) {
|
||||
|
||||
Object consumer = new Object();
|
||||
MessageLog messageLog = new MessageLog();
|
||||
try (LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||
.load(bp, programName, tool.getProject(), destFolder.getPathname(), loadSpec,
|
||||
options, messageLog, consumer, monitor)) {
|
||||
|
||||
ImporterSettings settings = new ImporterSettings(bp, importName, tool.getProject(),
|
||||
projectRootPath, mirrorFsLayout, loadSpec, options, new Object(), messageLog,
|
||||
monitor);
|
||||
|
||||
try (LoadResults<? extends DomainObject> loadResults =
|
||||
loadSpec.getLoader().load(settings)) {
|
||||
loadResults.save(monitor);
|
||||
doPostImportProcessing(tool, programManager, fsrl, loadResults,
|
||||
messageLog.toString(), monitor);
|
||||
doPostImportProcessing(tool, programManager, loadResults, messageLog.toString(),
|
||||
monitor);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -432,14 +440,13 @@ public class ImporterUtilities {
|
|||
}
|
||||
catch (Exception e) {
|
||||
Msg.showError(ImporterUtilities.class, tool.getActiveWindow(), "Error Importing File",
|
||||
"Error importing file: " + fsrl.getName(), e);
|
||||
"Error importing file: %s (%s)".formatted(fsrl.getName(), e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<DomainFile> doPostImportProcessing(PluginTool pluginTool,
|
||||
ProgramManager programManager, FSRL fsrl,
|
||||
LoadResults<? extends DomainObject> loadResults, String importMessages,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
ProgramManager programManager, LoadResults<? extends DomainObject> loadResults,
|
||||
String importMessages, TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
boolean firstProgram = true;
|
||||
Set<DomainFile> importedFilesSet = new HashSet<>();
|
||||
|
@ -490,8 +497,13 @@ public class ImporterUtilities {
|
|||
Objects.requireNonNull(monitor);
|
||||
|
||||
MessageLog messageLog = new MessageLog();
|
||||
Object consumer = new Object();
|
||||
program.addConsumer(consumer);
|
||||
try (ByteProvider bp = fsService.getByteProvider(fsrl, false, monitor)) {
|
||||
loadSpec.getLoader().loadInto(bp, loadSpec, options, messageLog, program, monitor);
|
||||
ImporterSettings settings = new ImporterSettings(bp, bp.getName(), tool.getProject(),
|
||||
program.getDomainFile().getPathname(), false, loadSpec, options, consumer,
|
||||
messageLog, monitor);
|
||||
loadSpec.getLoader().loadInto(program, settings);
|
||||
displayResults(tool, program, program.getDomainFile(), messageLog.toString());
|
||||
|
||||
// Optionally echo loader message log to application.log
|
||||
|
@ -506,6 +518,9 @@ public class ImporterUtilities {
|
|||
Msg.showError(ImporterUtilities.class, null, "Error Importing File",
|
||||
"Error importing file " + fsrl.getName(), e);
|
||||
}
|
||||
finally {
|
||||
program.release(consumer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import ghidra.app.util.*;
|
|||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.app.util.opinion.Loader.ImporterSettings;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -70,16 +71,18 @@ public class LoadLibrariesOptionsDialog extends OptionsDialog {
|
|||
TaskLauncher.launchNonModal(TITLE, monitor -> {
|
||||
super.okCallback();
|
||||
Object consumer = new Object();
|
||||
MessageLog messageLog = new MessageLog();
|
||||
try (LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||
.load(provider, program.getDomainFile().getName(), tool.getProject(),
|
||||
program.getDomainFile().getParent().getPathname(), loadSpec,
|
||||
getOptions(), messageLog, consumer, monitor)) {
|
||||
MessageLog log = new MessageLog();
|
||||
ImporterSettings settings =
|
||||
new ImporterSettings(provider, program.getDomainFile().getName(), tool.getProject(),
|
||||
program.getDomainFile().getParent().getPathname(), false, loadSpec,
|
||||
getOptions(), consumer, log, monitor);
|
||||
try (LoadResults<? extends DomainObject> loadResults =
|
||||
loadSpec.getLoader().load(settings)) {
|
||||
|
||||
loadResults.save(monitor);
|
||||
|
||||
// Display results
|
||||
String importMessages = messageLog.toString();
|
||||
String importMessages = log.toString();
|
||||
if (!importMessages.isEmpty()) {
|
||||
if (!Loader.loggingDisabled) {
|
||||
Msg.info(ImporterUtilities.class, TITLE + ":\n" + importMessages);
|
||||
|
@ -112,7 +115,7 @@ public class LoadLibrariesOptionsDialog extends OptionsDialog {
|
|||
private static List<Option> getLoadLibraryOptions(ByteProvider provider, LoadSpec loadSpec) {
|
||||
List<Option> options = new ArrayList<>();
|
||||
for (Option option : loadSpec.getLoader()
|
||||
.getDefaultOptions(provider, loadSpec, null, false)) {
|
||||
.getDefaultOptions(provider, loadSpec, null, false, false)) {
|
||||
switch (option.getName()) {
|
||||
case LOAD_ONLY_LIBRARIES_OPTION_NAME:
|
||||
case LOAD_LIBRARY_OPTION_NAME:
|
||||
|
@ -121,6 +124,7 @@ public class LoadLibrariesOptionsDialog extends OptionsDialog {
|
|||
case LINK_SEARCH_FOLDER_OPTION_NAME:
|
||||
case LIBRARY_SEARCH_PATH_DUMMY_OPTION_NAME:
|
||||
case LIBRARY_DEST_FOLDER_OPTION_NAME:
|
||||
case MIRROR_LAYOUT_OPTION_NAME:
|
||||
options.add(option);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -57,6 +57,7 @@ public class BatchImportDialog extends DialogComponentProvider {
|
|||
|
||||
private static final String PREF_STRIPCONTAINER = "BATCHIMPORT.STRIPCONTAINER";
|
||||
private static final String PREF_STRIPLEADING = "BATCHIMPORT.STRIPLEADING";
|
||||
private static final String PREF_MIRRORFS = "BATCHIMPORT.MIRRORFS";
|
||||
private static final String LAST_IMPORT_DIR = "LastBatchImportDir";
|
||||
|
||||
/**
|
||||
|
@ -92,8 +93,13 @@ public class BatchImportDialog extends DialogComponentProvider {
|
|||
private ProgramManager programManager;
|
||||
private boolean stripLeading = getBooleanPref(PREF_STRIPLEADING, true);
|
||||
private boolean stripContainer = getBooleanPref(PREF_STRIPCONTAINER, false);
|
||||
private boolean mirrorFs = getBooleanPref(PREF_MIRRORFS, false);
|
||||
private boolean openAfterImporting = false;
|
||||
|
||||
private GCheckBox stripLeadingCb;
|
||||
private GCheckBox stripContainerCb;
|
||||
private GCheckBox mirrorFsCb;
|
||||
|
||||
private BatchImportTableModel tableModel;
|
||||
private GTable table;
|
||||
private JButton removeSourceButton;
|
||||
|
@ -315,18 +321,25 @@ public class BatchImportDialog extends DialogComponentProvider {
|
|||
outputChoicesPanel.setLayout(new BoxLayout(outputChoicesPanel, BoxLayout.LINE_AXIS));
|
||||
outputChoicesPanel.getAccessibleContext().setAccessibleName("Output Choices");
|
||||
|
||||
GCheckBox stripLeadingCb = new GCheckBox("Strip leading path", stripLeading);
|
||||
stripLeadingCb = new GCheckBox("Strip leading path", stripLeading);
|
||||
stripLeadingCb.addChangeListener(e -> setStripLeading(stripLeadingCb.isSelected()));
|
||||
stripLeadingCb.setToolTipText("The destination folder for imported files will not " +
|
||||
"include the source file's leading path");
|
||||
stripLeadingCb.getAccessibleContext().setAccessibleName("Strip Leading Path");
|
||||
|
||||
GCheckBox stripContainerCb = new GCheckBox("Strip container paths", stripContainer);
|
||||
stripContainerCb = new GCheckBox("Strip container paths", stripContainer);
|
||||
stripContainerCb.addChangeListener(e -> setStripContainer(stripContainerCb.isSelected()));
|
||||
stripContainerCb.setToolTipText(
|
||||
"The destination folder for imported files will not include any source path names");
|
||||
stripContainerCb.getAccessibleContext().setAccessibleName("Strip Container Paths");
|
||||
|
||||
mirrorFsCb = new GCheckBox("Mirror Filesystem", mirrorFs);
|
||||
mirrorFsCb.addChangeListener(e -> setMirrorFs(mirrorFsCb.isSelected()));
|
||||
mirrorFsCb.setToolTipText(
|
||||
"The imported files' project paths will mirror the filesystem rooted at the desination folder");
|
||||
mirrorFsCb.getAccessibleContext().setAccessibleName("Mirror Filesystem");
|
||||
setMirrorFs(mirrorFs); // needed to possibly disable other checkboxes
|
||||
|
||||
GCheckBox openAfterImportCb = new GCheckBox("Open after import", openAfterImporting);
|
||||
openAfterImportCb
|
||||
.addChangeListener(e -> setOpenAfterImporting(openAfterImportCb.isSelected()));
|
||||
|
@ -335,6 +348,7 @@ public class BatchImportDialog extends DialogComponentProvider {
|
|||
|
||||
outputChoicesPanel.add(stripLeadingCb);
|
||||
outputChoicesPanel.add(stripContainerCb);
|
||||
outputChoicesPanel.add(mirrorFsCb);
|
||||
if (programManager != null) {
|
||||
outputChoicesPanel.add(openAfterImportCb);
|
||||
}
|
||||
|
@ -464,7 +478,7 @@ public class BatchImportDialog extends DialogComponentProvider {
|
|||
protected void okCallback() {
|
||||
new TaskLauncher(
|
||||
new ImportBatchTask(batchInfo, destinationFolder,
|
||||
openAfterImporting ? programManager : null, stripLeading, stripContainer),
|
||||
openAfterImporting ? programManager : null, stripLeading, stripContainer, mirrorFs),
|
||||
getComponent());
|
||||
close();
|
||||
}
|
||||
|
@ -616,6 +630,13 @@ public class BatchImportDialog extends DialogComponentProvider {
|
|||
setBooleanPref(PREF_STRIPCONTAINER, stripContainer);
|
||||
}
|
||||
|
||||
private void setMirrorFs(boolean mirrorFs) {
|
||||
this.mirrorFs = mirrorFs;
|
||||
setBooleanPref(PREF_MIRRORFS, mirrorFs);
|
||||
stripContainerCb.setEnabled(!mirrorFs);
|
||||
stripContainerCb.setSelected(mirrorFs ? false : stripContainer);
|
||||
}
|
||||
|
||||
private void setMaxDepth(int newMaxDepth) {
|
||||
if (newMaxDepth == batchInfo.getMaxDepth()) {
|
||||
return;
|
||||
|
|
|
@ -26,6 +26,7 @@ import ghidra.app.util.Option;
|
|||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.app.util.opinion.Loader.ImporterSettings;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.framework.main.AppInfo;
|
||||
import ghidra.framework.model.*;
|
||||
|
@ -54,6 +55,7 @@ public class ImportBatchTask extends Task {
|
|||
private DomainFolder destFolder;
|
||||
private boolean stripLeadingPath = true;
|
||||
private boolean stripAllContainerPath = false;
|
||||
private boolean mirrorFs = false;
|
||||
private ProgramManager programManager;
|
||||
private int totalObjsImported;
|
||||
private int totalAppsImported;
|
||||
|
@ -71,9 +73,11 @@ public class ImportBatchTask extends Task {
|
|||
* @param stripAllContainerPath boolean true if each imported file's parent container
|
||||
* source path should be completely omitted when creating the destination project folder path.
|
||||
* (the imported file's path within its container is still used)
|
||||
* @param mirrorFs boolean true if the filesystem should be mirrored during import
|
||||
*/
|
||||
public ImportBatchTask(BatchInfo batchInfo, DomainFolder destFolder,
|
||||
ProgramManager programManager, boolean stripLeading, boolean stripAllContainerPath) {
|
||||
ProgramManager programManager, boolean stripLeading, boolean stripAllContainerPath,
|
||||
boolean mirrorFs) {
|
||||
super("Batch Import Task", true, true, false, false);
|
||||
|
||||
this.batchInfo = batchInfo;
|
||||
|
@ -82,6 +86,7 @@ public class ImportBatchTask extends Task {
|
|||
this.programManager = programManager;
|
||||
this.stripLeadingPath = stripLeading;
|
||||
this.stripAllContainerPath = stripAllContainerPath;
|
||||
this.mirrorFs = mirrorFs;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -146,11 +151,12 @@ public class ImportBatchTask extends Task {
|
|||
try {
|
||||
MessageLog messageLog = new MessageLog();
|
||||
Project project = AppInfo.getActiveProject();
|
||||
try (LoadResults<? extends DomainObject> loadResults = loadSpec.getLoader()
|
||||
.load(byteProvider, fixupProjectFilename(destInfo.second), project,
|
||||
destInfo.first.getPathname(), loadSpec,
|
||||
getOptionsFor(batchLoadConfig, loadSpec, byteProvider), messageLog,
|
||||
this, monitor)) {
|
||||
ImporterSettings settings = new ImporterSettings(byteProvider,
|
||||
fixupProjectFilename(destInfo.second), project, destInfo.first.getPathname(),
|
||||
mirrorFs, loadSpec, getOptionsFor(batchLoadConfig, loadSpec, byteProvider),
|
||||
this, messageLog, monitor);
|
||||
try (LoadResults<? extends DomainObject> loadResults =
|
||||
loadSpec.getLoader().load(settings)) {
|
||||
|
||||
// TODO: accumulate batch results
|
||||
if (loadResults != null) {
|
||||
|
@ -170,7 +176,8 @@ public class ImportBatchTask extends Task {
|
|||
catch (CancelledException e) {
|
||||
Msg.debug(this, "Batch Import cancelled");
|
||||
}
|
||||
catch (IOException | VersionException | IllegalArgumentException e) {
|
||||
catch (IOException | VersionException | IllegalArgumentException
|
||||
| InvalidNameException e) {
|
||||
Msg.error(this, "Import failed for " + batchLoadConfig.getPreferredFileName(), e);
|
||||
}
|
||||
}
|
||||
|
@ -234,13 +241,12 @@ public class ImportBatchTask extends Task {
|
|||
String userSrcPath = userSrc.toPrettyFullpathString().replace('|', '/');
|
||||
int filename = fullPath.lastIndexOf('/') + 1;
|
||||
int uas = userSrcPath.length();
|
||||
int container = uas + 1;
|
||||
|
||||
int leadStart = (stripLeadingPath == false) ? 0 : userSrcPath.lastIndexOf('/') + 1;
|
||||
int leadEnd = Math.min(filename, userSrcPath.length());
|
||||
String leading = (leadStart < filename) ? fullPath.substring(leadStart, leadEnd) : "";
|
||||
String containerPath = container < filename && !stripInteriorContainerPath
|
||||
? fullPath.substring(container, filename)
|
||||
String containerPath = uas < filename && !stripInteriorContainerPath
|
||||
? fullPath.substring(uas, filename)
|
||||
: "";
|
||||
String filenameStr = fullPath.substring(filename);
|
||||
String result = FSUtilities.appendPath(leading, containerPath, filenameStr);
|
||||
|
@ -250,6 +256,23 @@ public class ImportBatchTask extends Task {
|
|||
private Pair<DomainFolder, String> getDestinationInfo(BatchLoadConfig batchLoadConfig,
|
||||
DomainFolder rootDestinationFolder) {
|
||||
FSRL fsrl = batchLoadConfig.getFSRL();
|
||||
|
||||
if (mirrorFs) {
|
||||
FSRL containerFSRL = fsrl.getFS().getContainer();
|
||||
String path = containerFSRL != null ? containerFSRL.toPrettyFullpathString() : "/";
|
||||
path = path.replace('|', '/');
|
||||
try {
|
||||
DomainFolder batchDestFolder = ProjectDataUtils.createDomainFolderPath(
|
||||
rootDestinationFolder, stripLeadingPath ? "" : path);
|
||||
return new Pair<>(batchDestFolder, fsrl.getName());
|
||||
}
|
||||
catch (IOException | InvalidNameException e) {
|
||||
Msg.error(this, "Problem creating project folder root: " +
|
||||
rootDestinationFolder.getPathname() + ", subpath: " + path, e);
|
||||
}
|
||||
return new Pair<>(rootDestinationFolder, fsrl.getName());
|
||||
}
|
||||
|
||||
String pathStr = fsrlToPath(fsrl, batchLoadConfig.getUasi().getFSRL(), stripLeadingPath,
|
||||
stripAllContainerPath);
|
||||
String preferredName = batchLoadConfig.getPreferredFileName();
|
||||
|
@ -280,8 +303,8 @@ public class ImportBatchTask extends Task {
|
|||
|
||||
private List<Option> getOptionsFor(BatchLoadConfig batchLoadConfig, LoadSpec loadSpec,
|
||||
ByteProvider byteProvider) {
|
||||
List<Option> options =
|
||||
batchLoadConfig.getLoader().getDefaultOptions(byteProvider, loadSpec, null, false);
|
||||
List<Option> options = batchLoadConfig.getLoader()
|
||||
.getDefaultOptions(byteProvider, loadSpec, null, false, false);
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,86 +21,54 @@ package ghidra.app.util.opinion;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.AccessMode;
|
||||
import java.util.*;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.FileByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.importer.ProgramLoader;
|
||||
import ghidra.app.util.opinion.DecompileDebugXmlLoader.DecompileDebugProgramInfo;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.database.data.ProgramDataTypeManager;
|
||||
import ghidra.program.database.function.FunctionManagerDB;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataTypeComponent;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class DecompileDebugXmlLoaderTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
File decompileDebugTestFile;
|
||||
private DecompileDebugXmlLoader loader;
|
||||
private ProgramDB program;
|
||||
private File decompileDebugTestFile;
|
||||
private ProgramLoader.Builder programLoader;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
String DECOMPILE_DEBUG_TEST_FILE = "ghidra/app/util/opinion/decompile_debug_test.xml";
|
||||
decompileDebugTestFile = ResourceManager.getResourceFile(DECOMPILE_DEBUG_TEST_FILE);
|
||||
loader = new DecompileDebugXmlLoader();
|
||||
programLoader = ProgramLoader.builder()
|
||||
.source(decompileDebugTestFile)
|
||||
.loaders(DecompileDebugXmlLoader.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link ghidra.app.util.opinion.DecompileDebugFormatManager#getProgramInfo()}.
|
||||
*
|
||||
* @throws Exception if an error occurred
|
||||
*/
|
||||
@Test
|
||||
public void testDecompileDebugXmlLoad() {
|
||||
Collection<LoadSpec> loadSpecs;
|
||||
MessageLog log = new MessageLog();
|
||||
try {
|
||||
ByteProvider byteProvider =
|
||||
new FileByteProvider(decompileDebugTestFile, null, AccessMode.READ);
|
||||
loadSpecs = loader.findSupportedLoadSpecs(byteProvider);
|
||||
assertTrue("Expected single loader opinion", loadSpecs.size() == 1);
|
||||
|
||||
LoadSpec loadSpec = loadSpecs.iterator().next();
|
||||
List<Option> options = loader.getDefaultOptions(byteProvider, loadSpec, null, false);
|
||||
|
||||
LoadResults<? extends DomainObject> loadResults =
|
||||
loader.load(byteProvider, byteProvider.getName(), null, null, loadSpec, options,
|
||||
log, this, TaskMonitor.DUMMY);
|
||||
loadResults.getNonPrimary();
|
||||
program = (ProgramDB) loadResults.getPrimaryDomainObject(this);
|
||||
public void testDecompileDebugXmlLoad() throws Exception {
|
||||
try (LoadResults<Program> loadResults = programLoader.load()) {
|
||||
assertTrue("expected single loaded program", loadResults.size() == 1);
|
||||
}
|
||||
catch (IOException | CancelledException | VersionException e) {
|
||||
log.appendException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyProgramInfo() {
|
||||
public void testVerifyProgramInfo() throws Exception {
|
||||
DecompileDebugFormatManager mngr = new DecompileDebugFormatManager(decompileDebugTestFile);
|
||||
DecompileDebugProgramInfo progInfo;
|
||||
MessageLog log = new MessageLog();
|
||||
try {
|
||||
progInfo = mngr.getProgramInfo();
|
||||
DecompileDebugProgramInfo progInfo = mngr.getProgramInfo();
|
||||
assertEquals("Spec string should be parsed correctly", "x86:LE:64:default",
|
||||
progInfo.specString());
|
||||
assertEquals("Compiler string should be extracted separately", "gcc",
|
||||
|
@ -108,28 +76,12 @@ public class DecompileDebugXmlLoaderTest extends AbstractGhidraHeadedIntegration
|
|||
assertEquals("Memory offset should be parsed from the offset tag", "0x140022cb8",
|
||||
progInfo.offset());
|
||||
}
|
||||
catch (SAXException | IOException e) {
|
||||
log.appendException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyLoadedProgramBytes() {
|
||||
Collection<LoadSpec> loadSpecs;
|
||||
MessageLog log = new MessageLog();
|
||||
public void testVerifyLoadedProgramBytes() throws Exception {
|
||||
try (LoadResults<Program> loadResults = programLoader.load()) {
|
||||
Program program = loadResults.getPrimaryDomainObject(this);
|
||||
try {
|
||||
ByteProvider byteProvider =
|
||||
new FileByteProvider(decompileDebugTestFile, null, AccessMode.READ);
|
||||
loadSpecs = loader.findSupportedLoadSpecs(byteProvider);
|
||||
|
||||
LoadSpec loadSpec = loadSpecs.iterator().next();
|
||||
List<Option> options = loader.getDefaultOptions(byteProvider, loadSpec, null, false);
|
||||
LoadResults<? extends DomainObject> loadResults =
|
||||
loader.load(byteProvider, byteProvider.getName(), null, null, loadSpec, options,
|
||||
new MessageLog(), this, TaskMonitor.DUMMY);
|
||||
loadResults.getNonPrimary();
|
||||
program = (ProgramDB) loadResults.getPrimaryDomainObject(this);
|
||||
|
||||
assertEquals("gcc", program.getCompilerSpec().getCompilerSpecID().toString());
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
|
@ -138,47 +90,35 @@ public class DecompileDebugXmlLoaderTest extends AbstractGhidraHeadedIntegration
|
|||
|
||||
// Verify memory blocks
|
||||
verifyBlock(blocks[0], "decompile_debug_test.xml", true,
|
||||
getAddr(new BigInteger("140000000", 16)), 128);
|
||||
getAddr(program, new BigInteger("140000000", 16)), 128);
|
||||
verifyBlock(blocks[1], "__scrt_acquire_startup_lock", true,
|
||||
getAddr(new BigInteger("1400227f8", 16)), 1);
|
||||
getAddr(program, new BigInteger("1400227f8", 16)), 1);
|
||||
verifyBlock(blocks[2], "FUN_140022834", true,
|
||||
getAddr(new BigInteger("140022834", 16)), 1);
|
||||
getAddr(program, new BigInteger("140022834", 16)), 1);
|
||||
verifyBlock(blocks[3], "FUN_1400228fc", true,
|
||||
getAddr(new BigInteger("1400228fc", 16)), 1);
|
||||
getAddr(program, new BigInteger("1400228fc", 16)), 1);
|
||||
verifyBlock(blocks[4], "__scrt_release_startup_lock", true,
|
||||
getAddr(new BigInteger("140022994", 16)), 1);
|
||||
getAddr(program, new BigInteger("140022994", 16)), 1);
|
||||
verifyBlock(blocks[5], "__scrt_uninitialize_crt", true,
|
||||
getAddr(new BigInteger("1400229b8", 16)), 1);
|
||||
getAddr(program, new BigInteger("1400229b8", 16)), 1);
|
||||
verifyBlock(blocks[6], "decompile_debug_test.xml", true,
|
||||
getAddr(new BigInteger("140022cb8", 16)), 296);
|
||||
getAddr(program, new BigInteger("140022cb8", 16)), 296);
|
||||
verifyBlock(blocks[7], "decompile_debug_test.xml", true,
|
||||
getAddr(new BigInteger("140022df9", 16)), 39);
|
||||
|
||||
getAddr(program, new BigInteger("140022df9", 16)), 39);
|
||||
}
|
||||
finally {
|
||||
program.release(this);
|
||||
}
|
||||
catch (IOException | CancelledException | VersionException e) {
|
||||
log.appendException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyLoadedProgramDataTypes() {
|
||||
Collection<LoadSpec> loadSpecs;
|
||||
MessageLog log = new MessageLog();
|
||||
public void testVerifyLoadedProgramDataTypes() throws Exception {
|
||||
try (LoadResults<Program> loadResults = programLoader.load()) {
|
||||
Program program = loadResults.getPrimaryDomainObject(this);
|
||||
try {
|
||||
ByteProvider byteProvider =
|
||||
new FileByteProvider(decompileDebugTestFile, null, AccessMode.READ);
|
||||
loadSpecs = loader.findSupportedLoadSpecs(byteProvider);
|
||||
|
||||
LoadSpec loadSpec = loadSpecs.iterator().next();
|
||||
List<Option> options = loader.getDefaultOptions(byteProvider, loadSpec, null, false);
|
||||
LoadResults<? extends DomainObject> loadResults =
|
||||
loader.load(byteProvider, byteProvider.getName(), null, null, loadSpec, options,
|
||||
new MessageLog(), this, TaskMonitor.DUMMY);
|
||||
loadResults.getNonPrimary();
|
||||
program = (ProgramDB) loadResults.getPrimaryDomainObject(this);
|
||||
|
||||
// Verify data type generation
|
||||
ProgramDataTypeManager dtm = program.getDataTypeManager();
|
||||
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
|
||||
assertEquals("Category count didn't match. ", 2, dtm.getCategoryCount());
|
||||
|
||||
Iterator<Structure> structures = dtm.getAllStructures();
|
||||
|
@ -189,30 +129,19 @@ public class DecompileDebugXmlLoaderTest extends AbstractGhidraHeadedIntegration
|
|||
assertEquals("Array component name doesn't match", "e_magic", array.getFieldName());
|
||||
assertEquals("Array wasn't sized right", 2, array.getLength());
|
||||
}
|
||||
catch (IOException | CancelledException | VersionException e) {
|
||||
log.appendException(e);
|
||||
finally {
|
||||
program.release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyLoadedProgramFunctions() {
|
||||
Collection<LoadSpec> loadSpecs;
|
||||
MessageLog log = new MessageLog();
|
||||
public void verifyLoadedProgramFunctions() throws Exception {
|
||||
try (LoadResults<Program> loadResults = programLoader.load()) {
|
||||
Program program = loadResults.getPrimaryDomainObject(this);
|
||||
try {
|
||||
ByteProvider byteProvider =
|
||||
new FileByteProvider(decompileDebugTestFile, null, AccessMode.READ);
|
||||
loadSpecs = loader.findSupportedLoadSpecs(byteProvider);
|
||||
|
||||
LoadSpec loadSpec = loadSpecs.iterator().next();
|
||||
List<Option> options = loader.getDefaultOptions(byteProvider, loadSpec, null, false);
|
||||
LoadResults<? extends DomainObject> loadResults =
|
||||
loader.load(byteProvider, byteProvider.getName(), null, null, loadSpec, options,
|
||||
new MessageLog(), this, TaskMonitor.DUMMY);
|
||||
loadResults.getNonPrimary();
|
||||
program = (ProgramDB) loadResults.getPrimaryDomainObject(this);
|
||||
|
||||
// Verify function collection
|
||||
FunctionManagerDB funcMngr = program.getFunctionManager();
|
||||
FunctionManager funcMngr = program.getFunctionManager();
|
||||
assertEquals("Function count doesn't match", 19, funcMngr.getFunctionCount());
|
||||
|
||||
Iterator<Function> functions = funcMngr.getFunctions(true);
|
||||
|
@ -220,8 +149,9 @@ public class DecompileDebugXmlLoaderTest extends AbstractGhidraHeadedIntegration
|
|||
assertEquals("Function name needs to match.", "__scrt_acquire_startup_lock",
|
||||
function.getName());
|
||||
}
|
||||
catch (IOException | CancelledException | VersionException e) {
|
||||
log.appendException(e);
|
||||
finally {
|
||||
program.release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,7 +163,7 @@ public class DecompileDebugXmlLoaderTest extends AbstractGhidraHeadedIntegration
|
|||
assertEquals("Block length didn't match", length, block.getSize());
|
||||
}
|
||||
|
||||
private Address getAddr(BigInteger offset) {
|
||||
private Address getAddr(Program program, BigInteger offset) {
|
||||
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset.longValue());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.util.*;
|
|||
import org.jdom.*;
|
||||
import org.jdom.input.SAXBuilder;
|
||||
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
|
@ -37,7 +36,6 @@ import ghidra.file.formats.android.xml.AndroidXmlFileSystem;
|
|||
import ghidra.file.formats.zip.ZipFileSystem;
|
||||
import ghidra.formats.gfilesystem.FileSystemService;
|
||||
import ghidra.formats.gfilesystem.GFile;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -64,15 +62,15 @@ public class ApkLoader extends DexLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||
protected List<Loaded<Program>> loadProgram(ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
boolean success = false;
|
||||
List<Loaded<Program>> allLoadedPrograms = new ArrayList<>();
|
||||
int dexIndex = 1;//DEX file numbering starts at 1
|
||||
try (ZipFileSystem zipFS = openAPK(provider, monitor)) {
|
||||
try (ZipFileSystem zipFS = openAPK(settings.provider(), monitor)) {
|
||||
while (!monitor.isCancelled()) {
|
||||
GFile classesDexFile =
|
||||
zipFS.lookup("/" + "classes" + (dexIndex == 1 ? "" : dexIndex) + ".dex");
|
||||
|
@ -81,16 +79,17 @@ public class ApkLoader extends DexLoader {
|
|||
break;
|
||||
}
|
||||
|
||||
monitor.setMessage(
|
||||
"Loading " + classesDexFile.getName() + " from " + programName + "...");
|
||||
monitor.setMessage("Loading " + classesDexFile.getName() + " from " +
|
||||
settings.importName() + "...");
|
||||
|
||||
try (ByteProvider dexProvider =
|
||||
zipFS.getByteProvider(classesDexFile, monitor)) {
|
||||
// defer to the super class (DexLoader) to actually load the DEX file
|
||||
List<Loaded<Program>> loadedPrograms =
|
||||
super.loadProgram(dexProvider, classesDexFile.getName(), project,
|
||||
joinPaths(programFolderPath, programName), loadSpec, options, log,
|
||||
consumer, monitor);
|
||||
ImporterSettings newSettings = new ImporterSettings(dexProvider,
|
||||
classesDexFile.getName(), settings.project(), settings.projectRootPath(),
|
||||
settings.mirrorFsLayout(), settings.loadSpec(), settings.options(),
|
||||
settings.consumer(), settings.log(), settings.monitor());
|
||||
List<Loaded<Program>> loadedPrograms = super.loadProgram(newSettings);
|
||||
|
||||
allLoadedPrograms.addAll(loadedPrograms);
|
||||
}
|
||||
|
|
|
@ -72,21 +72,21 @@ public class DexLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
|
||||
|
||||
public void load(Program program, ImporterSettings settings) throws IOException {
|
||||
MessageLog log = settings.log();
|
||||
TaskMonitor monitor = settings.monitor();
|
||||
monitor.setMessage(getMonitorMessagePrimary());
|
||||
try {
|
||||
Address start = program.getAddressFactory().getDefaultAddressSpace().getAddress(0x0);
|
||||
long length = provider.length();
|
||||
long length = settings.provider().length();
|
||||
|
||||
try (InputStream inputStream = provider.getInputStream(0)) {
|
||||
try (InputStream inputStream = settings.provider().getInputStream(0)) {
|
||||
program.getMemory()
|
||||
.createInitializedBlock(getMemoryBlockName(), start, inputStream, length,
|
||||
monitor, false);
|
||||
}
|
||||
|
||||
BinaryReader reader = new BinaryReader(provider, true);
|
||||
BinaryReader reader = new BinaryReader(settings.provider(), true);
|
||||
DexHeader header = DexHeaderFactory.getDexHeader(reader);
|
||||
|
||||
monitor.setMessage(getMonitorMessageSecondary());
|
||||
|
@ -244,7 +244,7 @@ public class DexLoader extends AbstractProgramWrapperLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import ghidra.file.formats.ios.dyldcache.DyldCacheExtractor;
|
|||
import ghidra.file.formats.ios.dyldcache.DyldCacheFileSystem;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.listing.Group;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -77,14 +76,14 @@ public class DyldCacheExtractLoader extends MachoLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
|
||||
public void load(Program program, ImporterSettings settings) throws IOException {
|
||||
|
||||
try {
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
MachoExtractProgramBuilder.buildProgram(program, provider, fileBytes, false, log,
|
||||
monitor);
|
||||
addOptionalComponents(program, options, log, monitor);
|
||||
FileBytes fileBytes =
|
||||
MemoryBlockUtils.createFileBytes(program, settings.provider(), settings.monitor());
|
||||
MachoExtractProgramBuilder.buildProgram(program, settings.provider(), fileBytes, false,
|
||||
settings.log(), settings.monitor());
|
||||
addOptionalComponents(program, settings.options(), settings.log(), settings.monitor());
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return;
|
||||
|
@ -98,20 +97,20 @@ public class DyldCacheExtractLoader extends MachoLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||
List<Option> options, MessageLog log, Program program, TaskMonitor monitor)
|
||||
protected void loadProgramInto(Program program, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
FSRL fsrl = provider.getFSRL();
|
||||
FSRL fsrl = settings.provider().getFSRL();
|
||||
Group[] children = program.getListing().getDefaultRootModule().getChildren();
|
||||
if (Arrays.stream(children).anyMatch(e -> e.getName().contains(fsrl.getPath()))) {
|
||||
log.appendMsg("%s has already been added".formatted(fsrl.getPath()));
|
||||
settings.log().appendMsg("%s has already been added".formatted(fsrl.getPath()));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
MachoExtractProgramBuilder.buildProgram(program, provider, fileBytes, true, log,
|
||||
monitor);
|
||||
addOptionalComponents(program, options, log, monitor);
|
||||
FileBytes fileBytes =
|
||||
MemoryBlockUtils.createFileBytes(program, settings.provider(), settings.monitor());
|
||||
MachoExtractProgramBuilder.buildProgram(program, settings.provider(), fileBytes, true,
|
||||
settings.log(), settings.monitor());
|
||||
addOptionalComponents(program, settings.options(), settings.log(), settings.monitor());
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return;
|
||||
|
@ -141,7 +140,7 @@ public class DyldCacheExtractLoader extends MachoLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
List<Option> list = new ArrayList<>();
|
||||
list.add(new Option(LIBOBJC_OPTION_NAME, !loadIntoProgram && LIBOBJC_OPTION_DEFAULT,
|
||||
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-libobjc"));
|
||||
|
@ -185,19 +184,18 @@ public class DyldCacheExtractLoader extends MachoLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean isLoadLibraries(List<Option> options) {
|
||||
protected boolean isLoadLibraries(ImporterSettings settings) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldSearchAllPaths(Program program, List<Option> options, MessageLog log) {
|
||||
protected boolean shouldSearchAllPaths(Program program, ImporterSettings settings) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms,
|
||||
ImporterSettings settings) throws CancelledException, IOException {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
|
|
@ -21,16 +21,13 @@ import java.util.*;
|
|||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.file.formats.ios.fileset.MachoFileSetExtractor;
|
||||
import ghidra.formats.gfilesystem.FSRL;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.listing.Group;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A {@link Loader} for Mach-O file set entries extracted by Ghidra from a Mach-O file set
|
||||
|
@ -52,13 +49,13 @@ public class MachoFileSetExtractLoader extends MachoLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
|
||||
public void load(Program program, ImporterSettings settings) throws IOException {
|
||||
|
||||
try {
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
MachoExtractProgramBuilder.buildProgram(program, provider, fileBytes, false, log,
|
||||
monitor);
|
||||
FileBytes fileBytes =
|
||||
MemoryBlockUtils.createFileBytes(program, settings.provider(), settings.monitor());
|
||||
MachoExtractProgramBuilder.buildProgram(program, settings.provider(), fileBytes, false,
|
||||
settings.log(), settings.monitor());
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return;
|
||||
|
@ -72,19 +69,19 @@ public class MachoFileSetExtractLoader extends MachoLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog log, Program program, TaskMonitor monitor)
|
||||
protected void loadProgramInto(Program program, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
FSRL fsrl = provider.getFSRL();
|
||||
FSRL fsrl = settings.provider().getFSRL();
|
||||
Group[] children = program.getListing().getDefaultRootModule().getChildren();
|
||||
if (Arrays.stream(children).anyMatch(e -> e.getName().contains(fsrl.getPath()))) {
|
||||
log.appendMsg("%s has already been added".formatted(fsrl.getPath()));
|
||||
settings.log().appendMsg("%s has already been added".formatted(fsrl.getPath()));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
MachoExtractProgramBuilder.buildProgram(program, provider, fileBytes, true, log,
|
||||
monitor);
|
||||
FileBytes fileBytes =
|
||||
MemoryBlockUtils.createFileBytes(program, settings.provider(), settings.monitor());
|
||||
MachoExtractProgramBuilder.buildProgram(program, settings.provider(), fileBytes, true,
|
||||
settings.log(), settings.monitor());
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return;
|
||||
|
@ -114,24 +111,23 @@ public class MachoFileSetExtractLoader extends MachoLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isLoadLibraries(List<Option> options) {
|
||||
protected boolean isLoadLibraries(ImporterSettings settings) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldSearchAllPaths(Program program, List<Option> options, MessageLog log) {
|
||||
protected boolean shouldSearchAllPaths(Program program, ImporterSettings settings) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms,
|
||||
ImporterSettings settings) throws CancelledException, IOException {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,12 +106,11 @@ public class DumpFileLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("hiding")
|
||||
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
protected void load(Program program, ImporterSettings settings)
|
||||
throws CancelledException, IOException {
|
||||
this.log = log;
|
||||
parseDumpFile(provider, program, options, loadSpec, monitor);
|
||||
this.log = settings.log();
|
||||
parseDumpFile(settings.provider(), program, settings.options(), settings.loadSpec(),
|
||||
settings.monitor());
|
||||
}
|
||||
|
||||
private void parseDumpFile(ByteProvider provider, Program program, List<Option> options,
|
||||
|
@ -300,7 +299,7 @@ public class DumpFileLoader extends AbstractProgramWrapperLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean isLoadIntoProgram) {
|
||||
DomainObject domainObject, boolean isLoadIntoProgram, boolean mirrorFsLayout) {
|
||||
List<Option> options = new ArrayList<>();
|
||||
try {
|
||||
int size = loadSpec.getLanguageCompilerSpec().getLanguage().getDefaultSpace().getSize();
|
||||
|
|
|
@ -22,6 +22,7 @@ import ghidra.app.util.*;
|
|||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.app.util.opinion.Loader.ImporterSettings;
|
||||
import ghidra.file.formats.dump.DumpFile;
|
||||
import ghidra.file.formats.dump.DumpFileReader;
|
||||
import ghidra.framework.store.LockException;
|
||||
|
@ -79,6 +80,7 @@ public class Apport extends DumpFile {
|
|||
private void createBlocksFromElf(LoadSpec loadSpec, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
Object consumer = new Object();
|
||||
try (
|
||||
DecodedProvider provider =
|
||||
new DecodedProvider(this, reader.getByteProvider(), monitor)) {
|
||||
|
@ -86,7 +88,13 @@ public class Apport extends DumpFile {
|
|||
Option base = new Option(ElfLoaderOptionsFactory.IMAGE_BASE_OPTION_NAME,
|
||||
Long.toHexString(header.getMemoryInfo(0).getBaseAddress()));
|
||||
options.add(base);
|
||||
elfLoader.load(provider, loadSpec, options, program, monitor, log);
|
||||
program.addConsumer(consumer);
|
||||
ImporterSettings settings = new ImporterSettings(provider, program.getName(), null,
|
||||
null, false, loadSpec, options, consumer, log, monitor);
|
||||
elfLoader.load(program, settings);
|
||||
}
|
||||
finally {
|
||||
program.release(consumer);
|
||||
}
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
|
|
|
@ -62,12 +62,17 @@ public class DumpPeShim extends PeLoader {
|
|||
return;
|
||||
}
|
||||
program.setEffectiveImageBase(minAddress);
|
||||
Object consumer = new Object();
|
||||
try {
|
||||
load(provider, loadSpec, options, program, monitor, log);
|
||||
program.addConsumer(consumer);
|
||||
ImporterSettings settings = new ImporterSettings(provider, program.getName(), null,
|
||||
null, false, loadSpec, options, consumer, log, monitor);
|
||||
load(program, settings);
|
||||
monitor.checkCancelled();
|
||||
}
|
||||
finally {
|
||||
program.setEffectiveImageBase(null);
|
||||
program.release(consumer);
|
||||
}
|
||||
|
||||
shiftModule();
|
||||
|
|
|
@ -30,7 +30,6 @@ import ghidra.app.util.bin.ByteProvider;
|
|||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -174,32 +173,27 @@ public class SarifLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||
protected List<Loaded<Program>> loadProgram(ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
|
||||
//throw new RuntimeException("SARIF importer supports only 'Add To Program'");
|
||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||
LanguageCompilerSpecPair pair = settings.loadSpec().getLanguageCompilerSpec();
|
||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||
CompilerSpec importerCompilerSpec =
|
||||
importerLanguage.getCompilerSpecByID(pair.compilerSpecID);
|
||||
|
||||
ParseResult result = parse(provider, log);
|
||||
ParseResult result = parse(settings.provider(), settings.log());
|
||||
|
||||
Address imageBase = null;
|
||||
if (result.lastInfo.imageBase != null) {
|
||||
imageBase = importerLanguage.getAddressFactory().getAddress(result.lastInfo.imageBase);
|
||||
}
|
||||
Program prog = createProgram(provider, programName, imageBase, getName(), importerLanguage,
|
||||
importerCompilerSpec, consumer);
|
||||
List<Loaded<Program>> loadedList =
|
||||
List.of(new Loaded<>(prog, programName, project, programFolderPath, consumer));
|
||||
Program prog = createProgram(imageBase, settings);
|
||||
List<Loaded<Program>> loadedList = List.of(new Loaded<>(prog, settings));
|
||||
boolean success = false;
|
||||
try {
|
||||
success = doImport(result.lastSarifMgr, options, log, prog, monitor, false);
|
||||
success = doImport(result.lastSarifMgr, settings.options(), settings.log(), prog,
|
||||
settings.monitor(), false);
|
||||
if (success) {
|
||||
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
||||
createDefaultMemoryBlocks(prog, settings);
|
||||
return loadedList;
|
||||
}
|
||||
throw new LoadException("Failed to load");
|
||||
|
@ -212,11 +206,11 @@ public class SarifLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
||||
protected void loadProgramInto(Program prog, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
File file = provider.getFile();
|
||||
doImport(new ProgramSarifMgr(prog, file, log), options, log, prog, monitor, true);
|
||||
File file = settings.provider().getFile();
|
||||
doImport(new ProgramSarifMgr(prog, file, settings.log()), settings.options(),
|
||||
settings.log(), prog, settings.monitor(), true);
|
||||
}
|
||||
|
||||
private boolean doImportWork(final ProgramSarifMgr mgr, final List<Option> options,
|
||||
|
@ -313,7 +307,7 @@ public class SarifLoader extends AbstractProgramLoader {
|
|||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
return new SarifProgramOptions().getOptions(loadIntoProgram);
|
||||
}
|
||||
|
||||
|
@ -323,7 +317,8 @@ public class SarifLoader extends AbstractProgramLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
|
||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program) {
|
||||
try {
|
||||
new SarifProgramOptions().setOptions(options);
|
||||
}
|
||||
|
|
|
@ -20,10 +20,8 @@ import java.math.BigInteger;
|
|||
import java.util.*;
|
||||
|
||||
import ghidra.app.cmd.register.SetRegisterCmd;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.javaclass.format.*;
|
||||
import ghidra.javaclass.format.attributes.CodeAttribute;
|
||||
|
@ -89,10 +87,9 @@ public class JavaLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
|
||||
public void load(Program program, ImporterSettings settings) throws IOException {
|
||||
try {
|
||||
doLoad(provider, program, monitor);
|
||||
doLoad(settings.provider(), program, settings.monitor());
|
||||
}
|
||||
catch (LockException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -111,11 +108,6 @@ public class JavaLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
}
|
||||
|
||||
public void load(ByteProvider provider, Program program, TaskMonitor monitor)
|
||||
throws IOException {
|
||||
load(provider, null, null, program, monitor, null);
|
||||
}
|
||||
|
||||
private void doLoad(ByteProvider provider, Program program, TaskMonitor monitor)
|
||||
throws LockException, MemoryConflictException, AddressOverflowException,
|
||||
CancelledException, DuplicateNameException, IOException {
|
||||
|
|
|
@ -66,6 +66,7 @@ for common use cases.
|
|||
[<a href="#-scriptlog-path-to-script-log-file">-scriptlog <path to script log file></a>]
|
||||
[<a href="#-log-path-to-log-file">-log <path to log file></a>]
|
||||
[<a href="#-overwrite">-overwrite</a>]
|
||||
[<a href="#-mirror">-overwrite</a>]
|
||||
[<a href="#-recursive-depth">-recursive [<depth>]</a>]
|
||||
[<a href="#-readonly">-readOnly</a>]
|
||||
[<a href="#-deleteproject">-deleteProject</a>]
|
||||
|
@ -254,6 +255,10 @@ contained within a version repository, and the [`-commit`][commit] option has no
|
|||
the overwrite will fail. Removing a versioned file is also subject to other permission and in-use
|
||||
restrictions which could also cause an overwrite failure.
|
||||
|
||||
### `-mirror`
|
||||
Applies to [-import][import] mode only. If present, the absolute filesystem path of each imported
|
||||
file is mirrored in the project, rooted at the specified [folder path][projectname].
|
||||
|
||||
### `-recursive [<depth>]`
|
||||
If present, enables recursive descent into directories and project sub-folders when a directory/
|
||||
folder has been specified in [`-import`][import] or [`-process`][process] modes.
|
||||
|
|
|
@ -80,7 +80,7 @@ public class ImporterPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
loadSpecs.add(new LoadSpec(peLoader, 0,
|
||||
new LanguageCompilerSpecPair("x86:LE:32:default", "gcc"), false));
|
||||
loadSpecs.add(new LoadSpec(peLoader, 0,
|
||||
new LanguageCompilerSpecPair("x86:LE:32:default", "borland"), false));
|
||||
new LanguageCompilerSpecPair("x86:LE:32:default", "borlandcpp"), false));
|
||||
loadSpecs.add(new LoadSpec(peLoader, 0,
|
||||
new LanguageCompilerSpecPair("x86:LE:32:System Management Mode", "default"), false));
|
||||
runSwing(() -> {
|
||||
|
|
|
@ -38,6 +38,7 @@ import ghidra.program.model.listing.CodeUnit;
|
|||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -300,7 +301,8 @@ public class ReferencesPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
|
||||
}
|
||||
|
||||
private void importFile(File file) throws CancelledException, VersionException, IOException {
|
||||
private void importFile(File file)
|
||||
throws CancelledException, VersionException, IOException, InvalidNameException {
|
||||
Project project = env.getProject();
|
||||
try (LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||
.source(file)
|
||||
|
|
|
@ -20,13 +20,11 @@ import java.util.*;
|
|||
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.AbstractProgramWrapperLoader;
|
||||
import ghidra.app.util.opinion.LoadSpec;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Provide class-level documentation that describes what this loader does.
|
||||
|
@ -52,18 +50,17 @@ public class SkeletonLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
protected void load(Program prgram, ImporterSettings settiings)
|
||||
throws CancelledException, IOException {
|
||||
|
||||
// Load the bytes from 'provider' into the 'program'.
|
||||
// Load the bytes from 'settings.provider()' into the 'program'.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean isLoadIntoProgram) {
|
||||
List<Option> list =
|
||||
super.getDefaultOptions(provider, loadSpec, domainObject, isLoadIntoProgram);
|
||||
DomainObject domainObject, boolean isLoadIntoProgram, boolean mirrorFsLayout) {
|
||||
List<Option> list = super.getDefaultOptions(provider, loadSpec, domainObject,
|
||||
isLoadIntoProgram, mirrorFsLayout);
|
||||
|
||||
// If this loader has custom options, add them to 'list'
|
||||
list.add(new Option("Option name goes here", "Default option value goes here"));
|
||||
|
@ -72,7 +69,8 @@ public class SkeletonLoader extends AbstractProgramWrapperLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
|
||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program) {
|
||||
|
||||
// If this loader has custom options, validate them here. Not all options require
|
||||
// validation.
|
||||
|
|