Merge remote-tracking branch 'origin/patch'

This commit is contained in:
ghidra1
2026-06-10 16:16:42 -04:00
44 changed files with 816 additions and 353 deletions

View File

@@ -36,7 +36,6 @@ import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.FileDataTypeManager;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
public class IsfServer extends Thread {
@@ -101,13 +100,13 @@ public class IsfServer extends Thread {
try {
DataTypeManager dtm;
if (ns.endsWith(".gdt")) {
dtm = openAsArchive(ns);
dtm = openAsDataTypeArchive(ns);
}
else if (ns.endsWith(".gzf")) {
dtm = openAsDatabase(ns);
dtm = openAsProgramDatabase(ns);
}
else {
dtm = openAsDomainFile(ns);
dtm = openAsProgramFile(ns);
}
managers.put(ns, dtm);
return dtm;
@@ -119,43 +118,43 @@ public class IsfServer extends Thread {
}
}
private DataTypeManager openAsDomainFile(String ns) throws Exception {
private DataTypeManager openAsProgramFile(String ns) throws Exception {
ProjectData projectData = project.getProjectData();
DomainFile df = projectData.getFile(ns);
if (!Program.class.isAssignableFrom(df.getDomainObjectClass())) {
throw new IOException("File does not correspond to Program content: " + ns);
}
// FIXME: Need to track and release Program instance after DTM use is complete (GP-6895)
Program program = (Program) df.getDomainObject(this, false, false, TaskMonitor.DUMMY);
return program.getDataTypeManager();
}
private DataTypeManager openAsArchive(String ns) throws Exception {
private DataTypeManager openAsDataTypeArchive(String ns) throws Exception {
File gdt = new File(ns);
return FileDataTypeManager.openFileArchive(gdt, false);
}
private DataTypeManager openAsDatabase(String ns) throws Exception {
private DataTypeManager openAsProgramDatabase(String ns) throws Exception {
File gzf = new File(ns);
TaskMonitor dummy = TaskMonitor.DUMMY;
PackedDatabase db = PackedDatabase.getPackedDatabase(gzf, dummy);
DBHandle dbh = db.openForUpdate(dummy);
ProgramDB p = null;
Program p;
boolean success = false;
try {
p = new ProgramDB(dbh, OpenMode.UPDATE, dummy, this);
}
catch (VersionException e) {
if (!e.isUpgradable()) {
throw new RuntimeException(p + " uses an older version and is not upgradable.");
}
p = new ProgramDB(dbh, OpenMode.UPGRADE, dummy, this);
success = true;
}
finally {
dbh.close();
}
dbh = db.openForUpdate(dummy);
p = new ProgramDB(dbh, OpenMode.UPGRADE, dummy, this);
if (!p.isChanged()) {
throw new RuntimeException(p + " uses an older version and was not upgraded.");
if (!success) {
dbh.close();
}
}
// FIXME: Need to track and release Program instance after DTM use is complete (GP-6895)
return p.getListing().getDataTypeManager();
}

View File

@@ -1,27 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.export;
import ghidra.app.util.exporter.AsciiExporter;
import ghidra.framework.model.DomainObject;
import ghidra.trace.model.Trace;
public class TraceViewAsciiExporter extends AsciiExporter {
@Override
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
return Trace.class.isAssignableFrom(domainObjectClass);
}
}

View File

@@ -1,27 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.export;
import ghidra.app.util.exporter.BinaryExporter;
import ghidra.framework.model.DomainObject;
import ghidra.trace.model.Trace;
public class TraceViewBinaryExporter extends BinaryExporter {
@Override
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
return Trace.class.isAssignableFrom(domainObjectClass);
}
}

View File

@@ -1,27 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.export;
import ghidra.app.util.exporter.HtmlExporter;
import ghidra.framework.model.DomainObject;
import ghidra.trace.model.Trace;
public class TraceViewHtmlExporter extends HtmlExporter {
@Override
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
return Trace.class.isAssignableFrom(domainObjectClass);
}
}

View File

@@ -1,27 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.export;
import ghidra.app.util.exporter.IntelHexExporter;
import ghidra.framework.model.DomainObject;
import ghidra.trace.model.Trace;
public class TraceViewIntelHexExporter extends IntelHexExporter {
@Override
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
return Trace.class.isAssignableFrom(domainObjectClass);
}
}

View File

@@ -1,56 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.export;
import java.util.*;
import java.util.stream.Collectors;
import ghidra.app.util.*;
import ghidra.app.util.exporter.XmlExporter;
import ghidra.framework.model.DomainObject;
import ghidra.trace.model.Trace;
// TODO: perhaps getApplicableExporters should use domainObject's class, not file's object class.
// TODO: Where un-supported, be less abrasive, e.g., present empty managers.
public class TraceViewXmlExporter extends XmlExporter {
private final Map<String, Boolean> hideOpts = Map.of(
"Properties", false,
"Relocation Table", false,
"External Libraries", false);
@Override
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
return Trace.class.isAssignableFrom(domainObjectClass);
}
@Override
public List<Option> getOptions(DomainObjectService domainObjectService) {
List<Option> options = super.getOptions(domainObjectService);
return options.stream()
.filter(o -> !hideOpts.keySet().contains(o.getName()))
.collect(Collectors.toList());
}
@Override
public void setOptions(List<Option> options) throws OptionException {
List<Option> opts = new ArrayList<>(options);
options.stream().filter(o -> !hideOpts.keySet().contains(o.getName())).forEach(opts::add);
for (Map.Entry<String, Boolean> ent : hideOpts.entrySet()) {
opts.add(Option.newBoolean(ent.getKey()).value(ent.getValue()).build());
}
super.setOptions(opts);
}
}

View File

@@ -0,0 +1,197 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.export;
import java.io.File;
import java.util.Map;
import docking.ActionContext;
import docking.action.builder.ActionBuilder;
import docking.tool.ToolConstants;
import docking.widgets.OptionDialog;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.filechooser.GhidraFileChooserMode;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.utils.GztExporter;
import ghidra.app.plugin.core.help.AboutDomainObjectUtils;
import ghidra.app.services.CodeViewerService;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.framework.preferences.Preferences;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramSelection;
import ghidra.trace.model.Trace;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.util.Swing;
import ghidra.util.filechooser.ExtensionFileFilter;
import ghidra.util.filechooser.GhidraFileFilter;
import ghidra.util.task.TaskLauncher;
//@formatter:off
@PluginInfo(
status = PluginStatus.RELEASED,
category = PluginCategoryNames.DEBUGGER,
packageName = DebuggerPluginPackage.NAME,
shortDescription = "Export Debugger Trace",
description = "This plugin exports a Debugger Trace to an external file.",
servicesRequired = {
DebuggerTraceManagerService.class
}
)
//@formatter:on
public class TraceExportPlugin extends Plugin {
private DebuggerTraceManagerService traceMgrSvc;
public TraceExportPlugin(PluginTool tool) {
super(tool);
createToolAction();
}
@Override
protected void init() {
traceMgrSvc = tool.getService(DebuggerTraceManagerService.class);
}
private void createToolAction() {
new ActionBuilder("Export Trace", getName())
.description("Export Debbuger Trace as compressed GZT file.")
// .helpLocation(new HelpLocation("ExporterPlugin", "Export"))
.menuPath(ToolConstants.MENU_FILE, "Export Trace...")
.menuGroup("DomainObjectSaveExport")
.enabledWhen(c -> getTrace(c) != null)
.onAction(c -> exportTrace(c))
.buildAndInstall(tool);
}
private Trace getTrace(ActionContext ctx) {
if (ctx instanceof ProgramActionContext programCtx) {
Program p = programCtx.getProgram();
if (p instanceof TraceProgramView traceProgrmView) {
return traceProgrmView.getTrace();
}
}
return traceMgrSvc.getCurrentTrace();
}
private void exportTrace(ActionContext ctx) {
Trace trace = getTrace(ctx);
if (trace == null) {
return;
}
File file = chooseDestinationFile(ctx);
if (file == null) {
return; // file chooser cancelled
}
File gztFile = file;
GztExporter exporter = new GztExporter();
TaskLauncher.launchModal("Export Trace", m -> {
exporter.export(gztFile, trace, null, m);
});
displaySummaryResults(trace, gztFile, exporter.getMessageLog());
}
private File chooseDestinationFile(ActionContext ctx) {
GhidraFileChooser chooser = new GhidraFileChooser(ctx.getSourceComponent());
chooser.setCurrentDirectory(getLastExportDirectory());
chooser.setTitle("Select Trace Output File");
chooser.setApproveButtonText("Export Trace");
chooser.setApproveButtonToolTipText("Export Debugger Trace");
chooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY);
chooser.setSelectedFileFilter(GhidraFileFilter.ALL);
chooser.setFileFilter(
new ExtensionFileFilter(GztExporter.EXTENSION, GztExporter.NAME));
File file;
while (true) {
file = chooser.getSelectedFile();
if (file == null) {
break;
}
setLastExportDirectory(file);
if (!file.getName().endsWith(GztExporter.SUFFIX)) {
file = new File(file.getParent(), file.getName() + GztExporter.SUFFIX);
}
if (!file.exists()) {
break; // continue with file return
}
if (!file.isFile()) {
chooser.setStatusText("Invalid File Selection");
continue;
}
int rc = OptionDialog.showYesNoCancelDialog(chooser.getComponent(),
"Overwrite Confirmation",
"Overwrite Trace export file?\n" + file);
if (rc == OptionDialog.YES_OPTION) {
break; // continue with file return
}
file = null; // don't overwrite
if (rc != OptionDialog.NO_OPTION) {
break; // continue with null return / export cancelled
}
}
chooser.dispose();
return file;
}
private File getLastExportDirectory() {
String lastDirStr = Preferences.getProperty(Preferences.LAST_EXPORT_DIRECTORY,
System.getProperty("user.home"), true);
return new File(lastDirStr);
}
private void setLastExportDirectory(File file) {
Preferences.setProperty(Preferences.LAST_EXPORT_DIRECTORY, file.getParent());
Preferences.store();
}
protected ProgramSelection getSelection() {
CodeViewerService service = tool.getService(CodeViewerService.class);
if (service != null) {
return service.getCurrentSelection();
}
return null;
}
private void displaySummaryResults(Trace trace, File outputFile, MessageLog log) {
StringBuffer resultsBuffer = new StringBuffer();
resultsBuffer.append("Destination file: " + outputFile.getAbsolutePath() + "\n\n");
resultsBuffer.append("Destination file Size: " + outputFile.length() + "\n");
resultsBuffer.append("Format: " + GztExporter.NAME + "\n\n");
resultsBuffer.append(log.toString());
Map<String, String> metadata = trace.getMetadata();
Swing.runLater(() -> {
AboutDomainObjectUtils.displayInformation(tool, trace.getDomainFile(), metadata,
"Trace Export Results Summary", resultsBuffer.toString(), null);
});
}
}

View File

@@ -625,18 +625,9 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
return newCurrent;
}
protected String getTraceDisplayName(Trace trace) {
DomainFile file = trace.getDomainFile();
String name = file.getName();
if (!name.isBlank()) {
return name;
}
return trace.getName();
}
protected void contextChanged() {
Trace trace = current.getTrace();
String itemName = trace == null ? "..." : getTraceDisplayName(trace);
String itemName = trace == null ? "..." : trace.getDomainFile().getName();
actionCloseTrace.getMenuBarData().setMenuItemName(CloseTraceAction.NAME_PREFIX + itemName);
actionSaveTrace.getMenuBarData().setMenuItemName(SaveTraceAction.NAME_PREFIX + itemName);
tool.contextChanged(null);

View File

@@ -0,0 +1,122 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.utils;
import java.io.File;
import java.io.IOException;
import java.util.List;
import ghidra.app.util.DomainObjectService;
import ghidra.app.util.Option;
import ghidra.app.util.exporter.Exporter;
import ghidra.app.util.exporter.ExporterException;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainObject;
import ghidra.program.model.address.AddressSetView;
import ghidra.trace.database.DBTrace;
import ghidra.util.HelpLocation;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class GztExporter extends Exporter {
public static final String EXTENSION = "gzt";
public static final String SUFFIX = "." + EXTENSION;
public static final String NAME = "Ghidra Trace Zip File";
public GztExporter() {
super(NAME, EXTENSION, new HelpLocation("ExporterPlugin", "gzt"));
}
@Override
public boolean canExportDomainFile(DomainFile domainFile) {
// Avoid exporting link-files or non-Trace files
return !domainFile.isLink() && canExportDomainObject(domainFile.getDomainObjectClass());
}
@Override
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
return DBTrace.class.isAssignableFrom(domainObjectClass);
}
@Override
public boolean equals(Object obj) {
return (obj instanceof GztExporter);
}
@Override
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet,
TaskMonitor monitor) {
if (!canExportDomainObject(domainObj.getClass())) {
throw new UnsupportedOperationException("only DBTrace objects are supported");
}
try {
file.delete();
domainObj.saveToPackedFile(file, monitor);
}
catch (UnsupportedOperationException e) {
log.appendMsg("Content does not support packed file export!");
log.appendException(e);
return false;
}
catch (CancelledException ce) {
return false;
}
catch (Exception e) {
log.appendMsg("Unexpected exception exporting file: " + e.getMessage());
return false;
}
return true;
}
@Override
public boolean export(File file, DomainFile domainFile, TaskMonitor monitor)
throws ExporterException, IOException {
if (!canExportDomainFile(domainFile)) {
throw new UnsupportedOperationException("only DBTrace files are supported");
}
try {
domainFile.packFile(file, monitor);
}
catch (CancelledException e) {
return false;
}
catch (Exception e) {
log.appendMsg("Unexpected exception exporting file: " + e.getMessage());
return false;
}
return true;
}
@Override
public List<Option> getOptions(DomainObjectService domainObjectService) {
return EMPTY_OPTIONS;
}
@Override
public void setOptions(List<Option> options) {
// no options for this exporter
}
/**
* Returns false. GZT export only supports entire database.
*/
@Override
public boolean supportsAddressRestrictedExport() {
return false;
}
}

View File

@@ -0,0 +1,177 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.utils;
import java.io.*;
import java.util.*;
import org.apache.commons.io.FilenameUtils;
import db.DBHandle;
import ghidra.app.util.Option;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.opinion.*;
import ghidra.framework.Application;
import ghidra.framework.data.OpenMode;
import ghidra.framework.model.DomainObject;
import ghidra.framework.store.db.PackedDatabase;
import ghidra.framework.store.local.ItemSerializer;
import ghidra.program.model.lang.LanguageNotFoundException;
import ghidra.program.model.listing.Program;
import ghidra.trace.database.DBTrace;
import ghidra.trace.database.DBTraceContentHandler;
import ghidra.trace.model.Trace;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
import utilities.util.FileUtilities;
/**
* Loads a packed Ghidra Trace file.
*/
public class GztLoader implements Loader {
public final static String GZT_NAME = "GZT Input Format";
@Override
public LoaderTier getTier() {
return LoaderTier.SPECIALIZED_TARGET_LOADER;
}
@Override
public int getTierPriority() {
return 0;
}
@Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program) {
if (options != null && options.size() > 0) {
return "GztLoader takes no options";
}
return null;
}
@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
return List.of();
}
@Override
public LoadResults<? extends DomainObject> load(ImporterSettings settings)
throws IOException, CancelledException, VersionException {
Trace trace = loadPackedTraceDatabase(settings.provider(), settings.importName(),
settings.consumer(), settings.monitor());
return new LoadResults<>(new Loaded<>(trace, settings));
}
private Trace loadPackedTraceDatabase(ByteProvider provider, String traceName,
Object consumer, TaskMonitor monitor)
throws IOException, CancelledException, VersionException, LanguageNotFoundException {
Trace trace;
File file = provider.getFile();
File tmpFile = null;
if (file == null) {
file = tmpFile = createTmpFile(provider, monitor);
}
try {
PackedDatabase packedDatabase = PackedDatabase.getPackedDatabase(file, true, monitor);
boolean success = false;
DBHandle dbh = null;
try {
if (!DBTraceContentHandler.TRACE_CONTENT_TYPE
.equals(packedDatabase.getContentType())) {
throw new IOException("File imported is not a Trace: " + traceName);
}
monitor.setMessage("Restoring " + provider.getName());
dbh = packedDatabase.open(monitor);
trace = new DBTrace(dbh, OpenMode.UPGRADE, monitor, consumer);
success = true;
}
finally {
if (!success) {
if (dbh != null) {
dbh.close(); // also disposes packed database object
}
else {
packedDatabase.dispose();
}
}
}
return trace;
}
finally {
if (tmpFile != null) {
tmpFile.delete();
}
}
}
@Override
public void loadInto(Program program, ImporterSettings settings)
throws IOException, LoadException, CancelledException {
throw new LoadException("Cannot add GZT to program");
}
@Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
List<LoadSpec> loadSpecs = new ArrayList<>();
if (isGztFile(provider)) {
loadSpecs.add(new LoadSpec(this, 0, false));
}
return loadSpecs;
}
@Override
public String getPreferredFileName(ByteProvider provider) {
return FilenameUtils.removeExtension(provider.getName());
}
private static File createTmpFile(ByteProvider provider, TaskMonitor monitor)
throws IOException {
File tmpFile = Application.createTempFile("ghidra_gzt_loader", null);
try (InputStream is = provider.getInputStream(0);
FileOutputStream fos = new FileOutputStream(tmpFile)) {
FileUtilities.copyStreamToStream(is, fos, monitor);
}
return tmpFile;
}
private static boolean isGztFile(ByteProvider provider) {
if (!provider.getName().toLowerCase().endsWith(".gzt")) {
return false;
}
boolean isGZT = false;
try (InputStream inputStream = provider.getInputStream(0)) {
isGZT = ItemSerializer.isPackedFile(inputStream);
}
catch (IOException e) {
// ignore
}
return isGZT;
}
@Override
public String getName() {
return GZT_NAME;
}
}

View File

@@ -823,7 +823,7 @@ public abstract class AbstractGhidraHeadedDebuggerTest
protected File pack(DomainObject object) throws Exception {
File tempDir = Files.createTempDirectory("ghidra-" + name.getMethodName()).toFile();
File pack = new File(tempDir, "obj" + System.identityHashCode(object) + ".gzf");
File pack = new File(tempDir, "obj" + System.identityHashCode(object) + ".gzt");
object.saveToPackedFile(pack, monitor);
return pack;
}

View File

@@ -236,9 +236,9 @@ public class ToyDBTraceBuilder implements AutoCloseable {
}
/**
* Open a .gzf compressed trace
* Open a .gzt packed trace
*
* @param file the .gzf file containing the trace
* @param file the .gzt file containing the trace
* @throws CancelledException never, since the monitor cannot be cancelled
* @throws VersionException if the trace's version is not as expected
* @throws LanguageNotFoundException if the trace's language cannot be found
@@ -936,7 +936,7 @@ public class ToyDBTraceBuilder implements AutoCloseable {
}
/**
* Save the trace to a temporary .gzf file
* Save the trace to a temporary packed .gzt file
*
* @return the new file
* @throws IOException if the trace could not be saved

View File

@@ -12,10 +12,10 @@
<BODY lang="EN-US">
<H1><A name="Export"></A>Exporting Files</H1>
<P>Ghidra provides an <I>Exporter</I> that allows a user to output program information into a
file in various formats.&nbsp;</P>
<P>Ghidra provides an <I>Exporter</I> that allows a user to output Program information into a
file in various formats.</P>
<P>Some of the formats the <I>Exporter</I> supports are:</P>
<P>Some of the Program file formats the <I>Exporter</I> supports are:</P>
<A name="Exporter_Formats">
@@ -25,8 +25,6 @@
<LI><A href="#c_cpp">C/C++</A></LI>
<LI><A href="#gzf">Ghidra Zip File (GZF)</A></LI>
<LI><A href="#gdt">Ghidra Data Type Archive File (GDT)</a></LI>
<LI><A href="#html">HTML</A></LI>
@@ -40,6 +38,12 @@
<LI><A href="#sarif">SARIF Export Format</A></LI>
</UL>
<P><I><IMG src="help/shared/note.png"> Content types other than Program may support
export and will present those export formats which are supported. Example: A
Debugger Trace may be exported as a Ghidra Zip Debugger Trace file (GZT). A data
type archive may be exported as a compressed Ghidra Data Type (GDT) archive file.
</P>
<H2>Export Action</H2>

View File

@@ -599,11 +599,23 @@ xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-
The global namespace is the default namespace.</P>
</BLOCKQUOTE>
<H2><A name="gdt"></A>gdt</H2>
<BLOCKQUOTE>
<P>File extension given to a compressed Ghidra Data Type Archive database file.</P>
</BLOCKQUOTE>
<H2><A name="gzf"></A>gzf</H2>
<BLOCKQUOTE>
<P>File extension given to Ghidra program database files that have been "zipped up".</P>
<P>File extension given to a compressed Ghidra Program database file.</P>
</BLOCKQUOTE>
<H2><A name="gzt"></A>gzt</H2>
<BLOCKQUOTE>
<P>File extension given to a compressed Ghidra Debugger Trace database file.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<P><A name="h"></A><FONT size="7"><B>H</B></FONT></P>

View File

@@ -38,6 +38,7 @@
<LI>Executable and Linking Format (ELF)</LI>
<LI>Ghidra Data Type Archive File (GDT)</LI>
<LI>Ghidra Zip File (GZF)</LI>
<LI>Ghidra Zip Debugger Trace File (GZT)</LI>
<LI>Intel Hex</LI>
<LI>Java Class File</LI>
<LI>Mac OS X Mach-O</LI>
@@ -96,10 +97,12 @@
<UL>
<LI><B>Project Window</B>: Drag a file from the system file explorer application and drop
onto the Ghidra Project Tree destination folder. Dropping onto the table view is not
supported. In the case of Ghidra Zip File (GZF) or Ghidra Data Type Archive (GDT) file
supported. In the case of Ghidra Zip File (GZF), Ghidra Zip Debugger Trace file (GZT),
or Ghidra Data Type Archive (GDT) file
imports, an immediate unpack can be performed wthout a popup dialog if the Front End option
<I>Enable simple GZF/GDT unpack</I> is enabled (
<B>Edit<IMG src="help/shared/arrow.gif">Tool Options...<IMG src="help/shared/arrow.gif">File Import<IMG src="help/shared/arrow.gif">Enable simple GZF/GDT unpack</B>).
<I>Enable simple GZF/GZT/GDT unpack</I> is enabled (
<B>Edit<IMG src="help/shared/arrow.gif">Tool Options...<IMG src="help/shared/arrow.gif">
File Import<IMG src="help/shared/arrow.gif">Enable simple GZF/GZT/GDT unpack</B>).
</LI>
<LI>or <B>Running Tool</B>: Drag a file from the system file explorer application and drop
@@ -274,9 +277,10 @@
folder, otherwise the root folder will be the default. The <B>...</B> will bring up a
dialog for changing the destination folder.</LI><BR>
<LI><B>Program Name</B> - This field specifies the name for the newly imported program.
<LI><B>Program Name</B> - This field specifies the name for the newly imported file into
the active project.
By default, it will be the name of the imported file with any format specific extenstion
removed (e.g., .xml, .gzf). Path information at the beginning of this field
removed (e.g., .xml, .gzf, .gzt, .gdt). Path information at the beginning of this field
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>

View File

@@ -317,7 +317,7 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
private void chooseDestinationFile() {
GhidraFileChooser chooser = new GhidraFileChooser(getComponent());
chooser.setSelectedFile(getLastExportDirectory());
chooser.setCurrentDirectory(getLastExportDirectory());
chooser.setTitle("Select Output File");
chooser.setApproveButtonText("Select Output File");
chooser.setApproveButtonToolTipText("Select File");

View File

@@ -18,19 +18,25 @@ package ghidra.app.plugin.core.exporter;
import java.awt.event.KeyEvent;
import java.util.List;
import docking.action.*;
import javax.swing.KeyStroke;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.action.builder.ActionBuilder;
import docking.tool.ToolConstants;
import ghidra.app.CorePluginPackage;
import ghidra.app.context.NavigatableActionContext;
import ghidra.app.context.NavigatableContextAction;
import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.services.CodeViewerService;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.framework.main.ApplicationLevelPlugin;
import ghidra.framework.main.FrontEndService;
import ghidra.framework.main.datatable.FrontendProjectTreeAction;
import ghidra.framework.main.datatable.ProjectDataContext;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramSelection;
@@ -45,7 +51,7 @@ import ghidra.util.HelpLocation;
description = "This plugin exports a program or datatype archive to an external file."
)
//@formatter:on
public class ExporterPlugin extends Plugin implements ApplicationLevelPlugin {
public class ExporterPlugin extends ProgramPlugin implements ApplicationLevelPlugin {
private FrontEndService frontEndService;
@@ -64,32 +70,39 @@ public class ExporterPlugin extends Plugin implements ApplicationLevelPlugin {
return; // do not add File menu Export Program action to front-end
}
DockingAction action = new NavigatableContextAction("Export Program", getName()) {
@Override
protected void actionPerformed(NavigatableActionContext context) {
Program program = context.getProgram();
DomainFile domainFile = program.getDomainFile();
ExporterDialog.showExporterDialog(tool, domainFile, program,
context.getSelection());
}
};
MenuData menuData =
new MenuData(new String[] { "&File", "Export Program..." }, "Import Export");
menuData.setMenuSubGroup("z"); // last in the "Save" group
action.setMenuBarData(menuData);
action.setKeyBindingData(new KeyBindingData(KeyEvent.VK_O, 0));
action.setHelpLocation(new HelpLocation("ExporterPlugin", "Export"));
action.setDescription(getPluginDescription().getDescription());
tool.addAction(action);
new ActionBuilder("Export Program", getName())
.description(getPluginDescription().getDescription())
.helpLocation(new HelpLocation("ExporterPlugin", "Export"))
.menuPath(ToolConstants.MENU_FILE, "Export Program...")
.menuGroup("DomainObjectSaveExport")
.keyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0))
.enabledWhen(c -> getProgram(c) != null)
.onAction(c -> doExport(c))
.buildAndInstall(tool);
}
protected ProgramSelection getSelection() {
CodeViewerService service = tool.getService(CodeViewerService.class);
if (service != null) {
return service.getCurrentSelection();
private Program getProgram(ActionContext ctx) {
if (ctx instanceof ProgramLocationActionContext programCtx) {
Program p = programCtx.getProgram();
if (p != null) {
return p;
}
}
return null;
return getCurrentProgram();
}
private ProgramSelection getProgramSelection(ActionContext ctx) {
if (ctx instanceof ProgramLocationActionContext programCtx) {
return programCtx.getSelection();
}
return getProgramSelection();
}
private void doExport(ActionContext ctx) {
Program program = getProgram(ctx);
DomainFile domainFile = program.getDomainFile();
ExporterDialog.showExporterDialog(tool, domainFile, program,
getProgramSelection(ctx));
}
private void createFrontEndAction() {

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -76,8 +76,23 @@ class MultiProgramManager implements TransactionListener {
};
}
void addProgram(Program p, ProgramLocator locator, int state) {
/**
* Add specified program.
* <p>
* NOTE: If this is the first program to be added it will be forced to CURRENT state.
*
* @param p program to be added
* @param locator program location source information
* @param state initial state
* @return true if program is the current program
*/
boolean addProgram(Program p, ProgramLocator locator, int state) {
if (programMap.isEmpty()) {
// Tool does not handle case where single open program is not current program
state = ProgramManager.OPEN_CURRENT;
}
addProgram(new ProgramInfo(p, locator, state != ProgramManager.OPEN_HIDDEN), state);
return state == ProgramManager.OPEN_CURRENT;
}
private void addProgram(ProgramInfo programInfo, int state) {

View File

@@ -484,8 +484,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager, Opti
}
Runnable r = () -> {
programMgr.addProgram(p, locator, state);
if (state == ProgramManager.OPEN_CURRENT) {
if (programMgr.addProgram(p, locator, state)) {
programMgr.saveLocation();
}
contextChanged();

View File

@@ -161,14 +161,22 @@ public class SourceFilesTablePlugin extends ProgramPlugin implements OptionsChan
}
private boolean isEnabled(ListingActionContext context) {
Program p = getCurrentProgram();
if (p == null || p != context.getProgram()) {
return false;
}
Address address = context.getAddress();
SourceFileManager manager = getCurrentProgram().getSourceFileManager();
SourceFileManager manager = p.getSourceFileManager();
return manager.getSourceMapEntries(address).size() > 0;
}
private void viewSourceFile(ListingActionContext context) {
Program p = getCurrentProgram();
if (p == null || p != context.getProgram()) {
return;
}
Address address = context.getAddress();
SourceFileManager manager = getCurrentProgram().getSourceFileManager();
SourceFileManager manager = p.getSourceFileManager();
List<SourceMapEntry> entries = manager.getSourceMapEntries(address);
if (entries.isEmpty()) {
return; // sanity check

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -30,7 +30,7 @@ import ghidra.util.task.TaskMonitor;
* An implementation of exporter that creates
* an Ascii representation of the program.
*/
public class AsciiExporter extends Exporter {
public class AsciiExporter extends ProgramExporter {
private ProgramTextOptions options;
/**
@@ -60,11 +60,14 @@ public class AsciiExporter extends Exporter {
log.clear();
if (!(domainObj instanceof Program)) {
Program program;
try {
program = getProgram(domainObj);
}
catch (ClassCastException e) {
log.appendMsg("Unsupported type: " + domainObj.getClass().getName());
return false;
}
Program program = (Program) domainObj;
getOptions(() -> program);
new ProgramTextWriter(file, program, addressSet, monitor, options, provider);

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -23,14 +23,15 @@ import ghidra.app.util.Option;
import ghidra.framework.model.DomainObject;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.HelpLocation;
import ghidra.util.task.TaskMonitor;
/**
* An {@link Exporter} that can export memory blocks as raw bytes
*/
public class BinaryExporter extends Exporter {
public class BinaryExporter extends ProgramExporter {
public BinaryExporter() {
super("Raw Bytes", "bin", new HelpLocation("ExporterPlugin", "binary"));
@@ -40,30 +41,27 @@ public class BinaryExporter extends Exporter {
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet,
TaskMonitor monitor) throws IOException, ExporterException {
if (!(domainObj instanceof Program)) {
Program program;
try {
program = getProgram(domainObj);
}
catch (ClassCastException e) {
log.appendMsg("Unsupported type: " + domainObj.getClass().getName());
return false;
}
Program program = (Program) domainObj;
Memory memory = program.getMemory();
AddressSetView initializedAddressSet = memory.getAllInitializedAddressSet();
if (addrSet == null) {
addrSet = memory;
addrSet = initializedAddressSet;
}
AddressSet set = new AddressSet(addrSet);
//skip blocks that are not initialized...
MemoryBlock[] blocks = memory.getBlocks();
for (int i = 0; i < blocks.length; ++i) {
if (!blocks[i].isInitialized()) {
set.delete(new AddressRangeImpl(blocks[i].getStart(), blocks[i].getEnd()));
}
else {
addrSet = addrSet.intersect(initializedAddressSet);
}
try (FileOutputStream fos = new FileOutputStream(file)) {
AddressRangeIterator iter = set.getAddressRanges();
AddressRangeIterator iter = addrSet.getAddressRanges();
while (iter.hasNext()) {
AddressRange range = iter.next();
byte[] mem = new byte[(int) range.getLength()];

View File

@@ -25,7 +25,6 @@ import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainObject;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.task.TaskMonitor;
@@ -101,34 +100,32 @@ abstract public class Exporter implements ExtensionPoint {
/**
* Returns true if this exporter is capable of exporting the given domain file/object content
* type. For example, some exporters have the ability to export programs, other exporters can
* export project data type archives.
* export other {@link DomainFile} or {@link DomainObject} types such as project data type
* archives.
* <p>
* NOTE: This method should only be used as a preliminary check, if neccessary, to identify
* NOTE: This method should only be used as a preliminary check, if necessary, to identify
* exporter implementations that are capable of handling a specified content type/class. Prior
* to export a final check should be performed based on the export or either a
* {@link DomainFile} or {@link DomainObject}:
* <p>
* {@link DomainFile} export - the method {@link #canExportDomainFile(DomainFile)} should be
* used to verify a direct project file export is possible using the
* {@link #export(File, DomainFile, TaskMonitor)} method.
* {@link #export(File, DomainFile, TaskMonitor)} method which may avoid opening the file first.
* <p>
* {@link DomainObject} export - the method {@link #canExportDomainObject(DomainObject)} should
* be used to verify an export of a specific object is possible using the
* be used to verify an export of a specific object is possible before using the
* {@link #export(File, DomainObject, AddressSetView, TaskMonitor)} method.
*
* avoid opening DomainFile when possible.
* @param domainObjectClass the class of the domain object to test for exporting.
* @return true if this exporter knows how to export the given domain object type.
*/
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
return Program.class.isAssignableFrom(domainObjectClass);
}
public abstract boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass);
/**
* Returns true if exporter can export the specified {@link DomainFile} without instantiating
* a {@link DomainObject}. This method should be used prior to exporting using the
* {@link #export(File, DomainFile, TaskMonitor)} method. All exporter capable of a
* {@link DomainFile} export must also support a export of a {@link DomainObject} so that any
* {@link #export(File, DomainFile, TaskMonitor)} method. All exporters capable of a
* {@link DomainFile} export must also support an export of a {@link DomainObject} so that any
* possible data modification/upgrade is included within resulting export.
*
* @param domainFile domain file

View File

@@ -47,7 +47,7 @@ public class GdtExporter extends Exporter {
@Override
public boolean canExportDomainFile(DomainFile domainFile) {
// Avoid exporting link-file itself
// Avoid exporting link-file itself or non-Datatype Archives
return !domainFile.isLink() && canExportDomainObject(domainFile.getDomainObjectClass());
}
@@ -59,6 +59,9 @@ public class GdtExporter extends Exporter {
@Override
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet,
TaskMonitor monitor) {
if (!canExportDomainObject(domainObj.getClass())) {
throw new UnsupportedOperationException("only DataTypeArchiveDB objects are supported");
}
try {
file.delete();
domainObj.saveToPackedFile(file, monitor);

View File

@@ -42,7 +42,7 @@ public class GzfExporter extends Exporter {
@Override
public boolean canExportDomainFile(DomainFile domainFile) {
// Avoid exporting link-file itself
// Avoid exporting link-file itself or non-Program files
return !domainFile.isLink() && canExportDomainObject(domainFile.getDomainObjectClass());
}
@@ -59,6 +59,9 @@ public class GzfExporter extends Exporter {
@Override
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet,
TaskMonitor monitor) {
if (!canExportDomainObject(domainObj.getClass())) {
throw new UnsupportedOperationException("only ProgramDB objects are supported");
}
try {
file.delete();
domainObj.saveToPackedFile(file, monitor);

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -30,7 +30,7 @@ import ghidra.util.task.TaskMonitor;
* An implementation of exporter that creates
* an HTML representation of the program.
*/
public class HtmlExporter extends Exporter {
public class HtmlExporter extends ProgramExporter {
private ProgramTextOptions options;
/**
@@ -58,11 +58,14 @@ public class HtmlExporter extends Exporter {
public boolean export(File file, DomainObject domainObj, AddressSetView addressSet,
TaskMonitor monitor) throws IOException, ExporterException {
if (!(domainObj instanceof Program)) {
Program program;
try {
program = getProgram(domainObj);
}
catch (ClassCastException e) {
log.appendMsg("Unsupported type: " + domainObj.getClass().getName());
return false;
}
Program program = (Program) domainObj;
getOptions(() -> program);
new ProgramTextWriter(file, program, addressSet, monitor, options, provider);

View File

@@ -44,7 +44,7 @@ import ghidra.util.task.TaskMonitor;
* cause only lines that match the max record size to be printed; any other
* bytes will be dropped. If this option is not set, every byte will be represented in the output.
*/
public class IntelHexExporter extends Exporter {
public class IntelHexExporter extends ProgramExporter {
/** Option allowing the user to select the address space */
protected Option addressSpaceOption;
@@ -151,7 +151,11 @@ public class IntelHexExporter extends Exporter {
log.clear();
if (!(domainObj instanceof Program program)) {
Program program;
try {
program = getProgram(domainObj);
}
catch (ClassCastException e) {
log.appendMsg("Unsupported type: " + domainObj.getClass().getName());
return false;
}

View File

@@ -41,7 +41,7 @@ import utilities.util.FileUtilities;
* WARNING: Programs written to disk with this exporter may be runnable on your native platform.
* Use caution when exporting potentially malicious programs.
*/
public class OriginalFileExporter extends Exporter {
public class OriginalFileExporter extends ProgramExporter {
private static final String USER_MODS_OPTION_NAME = "Export User Byte Modifications";
private static final boolean USER_MODS_OPTION_DEFAULT = true;
@@ -63,6 +63,11 @@ public class OriginalFileExporter extends Exporter {
return false;
}
@Override
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
return Program.class.isAssignableFrom(domainObjectClass);
}
@Override
public boolean canExportDomainObject(DomainObject domainObject) {
if (domainObject instanceof Program program) {
@@ -75,13 +80,15 @@ public class OriginalFileExporter extends Exporter {
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet,
TaskMonitor monitor) throws IOException, ExporterException {
if (!(domainObj instanceof Program)) {
log.appendMsg("Unsupported type: " + domainObj.getClass().getSimpleName());
Program program;
try {
program = getProgram(domainObj);
}
catch (ClassCastException e) {
log.appendMsg("Unsupported type: " + domainObj.getClass().getName());
return false;
}
Program program = (Program) domainObj;
List<FileBytes> allFileBytes = program.getMemory().getAllFileBytes();
if (allFileBytes.isEmpty()) {
log.appendMsg("Exporting a program with no file source bytes is not supported");

View File

@@ -0,0 +1,55 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.exporter;
import ghidra.framework.model.DomainObject;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
/**
* {@link ProgramExporter} provides an abstract {@link Exporter} implementation for
* {@link Program} objects.
*/
public abstract class ProgramExporter extends Exporter {
/**
* Constructs a new {@link Program} exporter.
* @param name the display name of this exporter
* @param extension the default extension for this exporter
* @param help the help location for this exporter
*/
protected ProgramExporter(String name, String extension, HelpLocation help) {
super(name, extension, help);
}
/**
* Get an exportable Program instance from the specified DomainObject instance.
* The specified instance must satisfy {@link #canExportDomainObject(Class)}.
* @param domainObj domain object from which a Program may be derived.
* @return Program instance
* @throws ClassCastException if domain object class does not satisfy
* {@link #canExportDomainObject(Class)}.
*/
protected Program getProgram(DomainObject domainObj) throws ClassCastException {
return (Program) domainObj;
}
@Override
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
return Program.class.isAssignableFrom(domainObjectClass);
}
}

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -33,7 +33,7 @@ import ghidra.util.task.TaskMonitor;
* An implementation of exporter that creates
* an XML representation of the program.
*/
public class XmlExporter extends Exporter {
public class XmlExporter extends ProgramExporter {
private XmlProgramOptions options = new XmlProgramOptions();
/**
@@ -62,11 +62,14 @@ public class XmlExporter extends Exporter {
log.clear();
if (!(domainObj instanceof Program)) {
log.appendMsg("Unsupported type: "+domainObj.getClass().getName());
Program program;
try {
program = getProgram(domainObj);
}
catch (ClassCastException e) {
log.appendMsg("Unsupported type: " + domainObj.getClass().getName());
return false;
}
Program program = (Program)domainObj;
if (addrSet == null) {
addrSet = program.getMemory();

View File

@@ -47,7 +47,7 @@ public class GdtLoader implements Loader {
@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
return Collections.emptyList();
return List.of();
}
@Override

View File

@@ -66,7 +66,7 @@ public class GzfLoader implements Loader {
@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
return Collections.emptyList();
return List.of();
}
@Override
@@ -131,7 +131,6 @@ public class GzfLoader implements Loader {
@Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
// TODO WRONG! try to parse a bit and tell for real
List<LoadSpec> loadSpecs = new ArrayList<>();
if (isGzfFile(provider)) {
loadSpecs.add(new LoadSpec(this, 0, false));

View File

@@ -86,7 +86,7 @@ public class ImporterPlugin extends Plugin
"This plugin manages importing files, including those contained within " +
"firmware/filesystem images.";
private static final String SIMPLE_UNPACK_OPTION = "Enable simple GZF/GDT unpack";
private static final String SIMPLE_UNPACK_OPTION = "Enable simple GZF/GZT/GDT unpack";
private static final boolean SIMPLE_UNPACK_OPTION_DEFAULT = false;
private DockingAction importAction;

View File

@@ -450,7 +450,7 @@ public class ImporterUtilities {
ProgramManager programManager, LoadResults<? extends DomainObject> loadResults,
String importMessages, TaskMonitor monitor) throws CancelledException {
boolean firstProgram = true;
boolean firstFile = true;
Set<DomainFile> importedFilesSet = new HashSet<>();
for (Loaded<? extends DomainObject> loaded : loadResults) {
monitor.checkCancelled();
@@ -460,7 +460,7 @@ public class ImporterUtilities {
try {
if (obj instanceof Program) {
if (programManager != null) {
int openState = firstProgram
int openState = firstFile
? ProgramManager.OPEN_CURRENT
: ProgramManager.OPEN_VISIBLE;
programManager.openProgram((Program) obj, openState);
@@ -477,16 +477,16 @@ public class ImporterUtilities {
}
}
}
if (firstProgram) {
if (firstFile) {
// currently we only show results for the imported program, not any libraries
displayResults(pluginTool, obj, df, importMessages);
displayResults(pluginTool, obj, importMessages);
// Optionally echo loader message log to application.log
if (!Loader.loggingDisabled && !importMessages.isEmpty()) {
Msg.info(ImporterUtilities.class, "Additional info:\n" + importMessages);
}
}
firstProgram = false;
firstFile = false;
importedFilesSet.add(df);
}
finally {
@@ -518,7 +518,7 @@ public class ImporterUtilities {
program.getDomainFile().getPathname(), false, loadSpec, options, consumer,
messageLog, monitor);
loadSpec.getLoader().loadInto(program, settings);
displayResults(tool, program, program.getDomainFile(), messageLog.toString());
displayResults(tool, program, messageLog.toString());
// Optionally echo loader message log to application.log
if (!Loader.loggingDisabled && messageLog.hasMessages()) {
@@ -538,18 +538,9 @@ public class ImporterUtilities {
}
private static void displayResults(PluginTool tool, DomainObject obj, DomainFile df,
String info) {
DomainFile domainFile = obj.getDomainFile();
Map<String, String> metadata = obj.getMetadata();
if (df != null) {
domainFile = df;
metadata = df.getMetadata();
}
private static void displayResults(PluginTool tool, DomainObject obj, String info) {
HelpLocation helpLocation = new HelpLocation(GenericHelpTopics.ABOUT, "About_Program");
AboutDomainObjectUtils.displayInformation(tool, domainFile, metadata,
AboutDomainObjectUtils.displayInformation(tool, obj.getDomainFile(), obj.getMetadata(),
"Import Results Summary", info, helpLocation);
}

View File

@@ -41,7 +41,7 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.*;
import util.CollectionUtils;
public class CppExporter extends Exporter {
public class CppExporter extends ProgramExporter {
public static final String CREATE_C_FILE = "Create C File (.c)";
public static final String CREATE_HEADER_FILE = "Create Header File (.h)";
@@ -90,13 +90,16 @@ public class CppExporter extends Exporter {
@Override
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet,
TaskMonitor monitor) throws IOException, ExporterException {
if (!(domainObj instanceof Program)) {
Program program;
try {
program = getProgram(domainObj);
}
catch (ClassCastException e) {
log.appendMsg("Unsupported type: " + domainObj.getClass().getName());
return false;
}
Program program = (Program) domainObj;
configureOptions(program);
configureFunctionTags(program);

View File

@@ -20,8 +20,8 @@ import java.io.IOException;
import java.util.List;
import ghidra.app.util.*;
import ghidra.app.util.exporter.Exporter;
import ghidra.app.util.exporter.ExporterException;
import ghidra.app.util.exporter.ProgramExporter;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.model.DomainObject;
import ghidra.program.model.address.AddressSetView;
@@ -36,7 +36,7 @@ import sarif.managers.ProgramSarifMgr;
* An implementation of exporter that creates
* an SARIF representation of the program.
*/
public class SarifExporter extends Exporter {
public class SarifExporter extends ProgramExporter {
private SarifProgramOptions options = new SarifProgramOptions();
/**
@@ -59,17 +59,21 @@ public class SarifExporter extends Exporter {
this.options.setOptions(options);
}
@Override
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet, TaskMonitor monitor)
throws IOException, ExporterException {
log.clear();
if (!(domainObj instanceof Program)) {
log.appendMsg("Unsupported type: "+domainObj.getClass().getName());
Program program;
try {
program = getProgram(domainObj);
}
catch (ClassCastException e) {
log.appendMsg("Unsupported type: " + domainObj.getClass().getName());
return false;
}
Program program = (Program)domainObj;
if (addrSet == null) {
addrSet = program.getMemory();

View File

@@ -276,8 +276,8 @@ public interface FolderItem {
/**
* Update the checkout version associated with this versioned item.
* @param checkoutId id corresponding to an existing checkout
* @param checkoutVersion
* @param user
* @param checkoutVersion current checkout version
* @param user user performing update
* @throws IOException if an IO error occurs.
*/
void updateCheckoutVersion(long checkoutId, int checkoutVersion, String user)
@@ -289,7 +289,7 @@ public interface FolderItem {
* @param version if this item is versioned, specifies the version to be output, otherwise
* -1 should be specified.
* @param monitor progress monitor
* @throws IOException
* @throws IOException if failed to save packed file
* @throws CancelledException if monitor cancels operation
*/
public void output(File outputFile, int version, TaskMonitor monitor)
@@ -297,6 +297,8 @@ public interface FolderItem {
/**
* Returns this instance after refresh or null if item no longer exists
* @return refreshed item
* @throws IOException if error occured during refresh
*/
public FolderItem refresh() throws IOException;

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,15 +15,15 @@
*/
package ghidra.framework.store.local;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import generic.jar.ResourceFile;
import ghidra.util.MonitoredInputStream;
import ghidra.util.exception.IOCancelledException;
import ghidra.util.task.TaskMonitor;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* <code>ItemDeserializer</code> facilitates the reading of a compressed data stream
* contained within a "packed" file. A "packed" file contains the following meta-data

View File

@@ -15,16 +15,16 @@
*/
package ghidra.framework.store.local;
import java.io.*;
import org.apache.commons.lang3.StringUtils;
import ghidra.framework.store.DataFileItem;
import ghidra.framework.store.FolderItem;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateFileException;
import ghidra.util.task.TaskMonitor;
import java.io.*;
import org.apache.commons.lang3.StringUtils;
/**
* <code>LocalDataFileItem</code> provides a FolderItem implementation
* for a local serialized data file. This implementation supports
@@ -186,8 +186,7 @@ public class LocalDataFileItem extends LocalFolderItem implements DataFileItem {
@Override
public void output(File outputFile, int version, TaskMonitor monitor) throws IOException {
throw new UnsupportedOperationException("Output not yet supported for DataFiles");
throw new IOException("Packed output not yet supported for DataFiles");
}
@Override

View File

@@ -600,13 +600,18 @@ public class LocalDatabaseItem extends LocalFolderItem implements DatabaseItem {
public void output(File outputFile, int version, TaskMonitor monitor)
throws CancelledException, IOException {
synchronized (fileSystem) {
String contentType = getContentType();
if (UnknownFolderItem.UNKNOWN_CONTENT_TYPE.equals(contentType)) {
throw new IOException("Unknown content type");
}
if (isVersioned) {
versionedDb.output(version, outputFile, getName(), DATABASE_FILE_TYPE,
getContentType(), monitor);
versionedDb.output(version, outputFile, getName(), DATABASE_FILE_TYPE, contentType,
monitor);
}
else {
privateDb.output(outputFile, getName(), DATABASE_FILE_TYPE, getContentType(),
monitor);
privateDb.output(outputFile, getName(), DATABASE_FILE_TYPE, contentType, monitor);
}
}
}

View File

@@ -117,7 +117,7 @@ public class LocalTextDataItem extends LocalFolderItem implements TextDataItem {
@Override
public void output(File outputFile, int version, TaskMonitor monitor) throws IOException {
throw new IOException("Output not supported");
throw new IOException("Packed output not supported for text data");
}
@Override

View File

@@ -115,7 +115,8 @@ public class LocalUnknownFolderItem extends LocalFolderItem implements UnknownFo
@Override
public void output(File outputFile, int version, TaskMonitor monitor) throws IOException {
throw new UnsupportedOperationException("Output not supported for UnknownFolderItems");
throw new UnsupportedOperationException(
"Packed output not supported for UnknownFolderItems");
}
@Override

View File

@@ -158,7 +158,7 @@ public interface DomainObject {
* Saves (i.e., serializes) the current content to a packed file.
* @param outputFile packed output file
* @param monitor progress monitor
* @throws IOException if an exception occurs
* @throws IOException if an error occurs during operation
* @throws CancelledException if the user cancels
* @throws UnsupportedOperationException if not supported by object implementation
*/

View File

@@ -50,6 +50,12 @@ public class SkeletonExporter extends Exporter {
return false;
}
@Override
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
// return Program.class.isAssignableFrom(domainObjectClass);
return false;
}
@Override
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet,
TaskMonitor monitor) throws ExporterException, IOException {