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