mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 04:05:39 +00:00
Merge remote-tracking branch 'origin/adamopolous_GT-2703' into Ghidra_9.0.2
This commit is contained in:
commit
39563bed8d
@ -306,6 +306,12 @@
|
|||||||
<LI><B>Address Space</B> - Specifies which address space to export as Intel Hex format
|
<LI><B>Address Space</B> - Specifies which address space to export as Intel Hex format
|
||||||
only supports one address space. This option will be intialized to the "default"
|
only supports one address space. This option will be intialized to the "default"
|
||||||
address space.</LI>
|
address space.</LI>
|
||||||
|
<LI><B>Record Size</B> - Specifies the size (in bytes) of each record in the
|
||||||
|
output file. The default 16.</LI>
|
||||||
|
<LI><B>Align To Record Size</B> - If checked, this will ensure that <b>only</b> records matching
|
||||||
|
the record size will be output. eg: if you set the record size to 16 but there are
|
||||||
|
18 bytes selected, you will see only one line of 16 bytes in the output; the remaining
|
||||||
|
2 bytes will be dropped.</LI>
|
||||||
</UL>
|
</UL>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 7.3 KiB |
@ -96,6 +96,15 @@ public class Option {
|
|||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override if you want to provide a custom widget for selecting your
|
||||||
|
* options.
|
||||||
|
* <p>
|
||||||
|
* Important! If you override this you MUST also override the {@link #copy()}
|
||||||
|
* method so it returns a new instance of your custom editor.
|
||||||
|
*
|
||||||
|
* @return the custom editor
|
||||||
|
*/
|
||||||
public Component getCustomEditorComponent() {
|
public Component getCustomEditorComponent() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -25,17 +25,17 @@ import ghidra.util.Msg;
|
|||||||
public class Compare {
|
public class Compare {
|
||||||
public static void compare(ArrayList<String> expectedList, File actualFile) throws Exception {
|
public static void compare(ArrayList<String> expectedList, File actualFile) throws Exception {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
BufferedReader reader = new BufferedReader(new FileReader(actualFile));
|
|
||||||
|
|
||||||
boolean hasFailure = false;
|
boolean hasFailure = false;
|
||||||
|
|
||||||
try {
|
try (BufferedReader reader = new BufferedReader(new FileReader(actualFile))) {
|
||||||
int excess = 0;
|
int excess = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
String actualLine = reader.readLine();
|
String actualLine = reader.readLine();
|
||||||
if (actualLine == null) {
|
if (actualLine == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index >= expectedList.size()) {
|
if (index >= expectedList.size()) {
|
||||||
++excess;
|
++excess;
|
||||||
continue;
|
continue;
|
||||||
@ -73,8 +73,5 @@ public class Compare {
|
|||||||
Assert.fail("One or more failures--see output for data");
|
Assert.fail("One or more failures--see output for data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
reader.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.util.exporter;
|
package ghidra.app.util.exporter;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Component;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import docking.widgets.textfield.HintTextField;
|
||||||
import ghidra.app.util.*;
|
import ghidra.app.util.*;
|
||||||
import ghidra.app.util.opinion.IntelHexRecord;
|
import ghidra.app.util.opinion.IntelHexRecord;
|
||||||
import ghidra.app.util.opinion.IntelHexRecordWriter;
|
import ghidra.app.util.opinion.IntelHexRecordWriter;
|
||||||
@ -29,18 +34,55 @@ import ghidra.program.model.mem.*;
|
|||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the current program (or program selection) as bytes in Intel Hex format.
|
||||||
|
* <p>
|
||||||
|
* The output defaults to lines of 16-bytes but this is configurable using the
|
||||||
|
* {@link #recordSizeOption} attribute. This allows users to select any record size
|
||||||
|
* up to the max of 0xFF. Users may also choose to <code>Drop Extra Bytes</code>, which will
|
||||||
|
* 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 Exporter {
|
||||||
protected final static int MAX_BYTES_PER_LINE = 0x00000010;
|
|
||||||
|
|
||||||
protected Option option;
|
|
||||||
|
|
||||||
|
/** Option allowing the user to select the address space */
|
||||||
|
protected Option addressSpaceOption;
|
||||||
|
|
||||||
|
/** Option allowing the user to select the number of bytes in each line of output */
|
||||||
|
protected RecordSizeOption recordSizeOption;
|
||||||
|
|
||||||
|
private static final int DEFAULT_RECORD_SIZE = 0x10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new Intel Hex exporter.
|
* Constructs a new Intel Hex exporter. This will use a record size of 16 (the default)
|
||||||
|
* and will export ALL bytes in the program or selection (even if the total length
|
||||||
|
* is not a multiple of 16.
|
||||||
*/
|
*/
|
||||||
public IntelHexExporter() {
|
public IntelHexExporter() {
|
||||||
this("Intel Hex", "hex", new HelpLocation("ExporterPlugin", "intel_hex"));
|
this("Intel Hex", "hex", new HelpLocation("ExporterPlugin", "intel_hex"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Intel Hex exporter with a custom record size.
|
||||||
|
*
|
||||||
|
* @param recordSize the record size to use when writing to the output file
|
||||||
|
* @param dropBytes if true, bytes at the end of the file that don't match the specified
|
||||||
|
* record size will be dropped
|
||||||
|
*/
|
||||||
|
public IntelHexExporter(int recordSize, boolean dropBytes) {
|
||||||
|
this("Intel Hex", "hex", new HelpLocation("ExporterPlugin", "intel_hex"));
|
||||||
|
recordSizeOption = new RecordSizeOption("Record Size", Integer.class);
|
||||||
|
recordSizeOption.setRecordSize(recordSize);
|
||||||
|
recordSizeOption.setDropBytes(dropBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param name the name of the exporter
|
||||||
|
* @param extension the extension to use for the output file
|
||||||
|
* @param help location of Ghidra help
|
||||||
|
*/
|
||||||
protected IntelHexExporter(String name, String extension, HelpLocation help) {
|
protected IntelHexExporter(String name, String extension, HelpLocation help) {
|
||||||
super(name, extension, help);
|
super(name, extension, help);
|
||||||
}
|
}
|
||||||
@ -55,16 +97,49 @@ public class IntelHexExporter extends Exporter {
|
|||||||
}
|
}
|
||||||
Program program = (Program) domainObject;
|
Program program = (Program) domainObject;
|
||||||
|
|
||||||
option = new Option("Address Space", program.getAddressFactory().getDefaultAddressSpace());
|
addressSpaceOption =
|
||||||
|
new Option("Address Space", program.getAddressFactory().getDefaultAddressSpace());
|
||||||
|
|
||||||
|
if (recordSizeOption == null) {
|
||||||
|
recordSizeOption = new RecordSizeOption("Record Size", Integer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
optionsList.add(addressSpaceOption);
|
||||||
|
optionsList.add(recordSizeOption);
|
||||||
|
|
||||||
optionsList.add(option);
|
|
||||||
return optionsList;
|
return optionsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOptions(List<Option> options) throws OptionException {
|
public void setOptions(List<Option> options) throws OptionException {
|
||||||
if (!options.isEmpty()) {
|
if (!options.isEmpty()) {
|
||||||
option = options.get(0);
|
addressSpaceOption = options.get(0);
|
||||||
|
recordSizeOption = (RecordSizeOption) options.get(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifier for a {@link HintTextField} that ensures input is a numeric value between
|
||||||
|
* 0 and 0xFF.
|
||||||
|
* <p>
|
||||||
|
* Input may be specified in either decimal or hex.
|
||||||
|
*/
|
||||||
|
private class BoundedIntegerVerifier extends InputVerifier {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(JComponent input) {
|
||||||
|
HintTextField field = (HintTextField) input;
|
||||||
|
String text = field.getText();
|
||||||
|
|
||||||
|
int val;
|
||||||
|
try {
|
||||||
|
val = Integer.decode(text);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val <= 0xFF && val >= 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,33 +159,31 @@ public class IntelHexExporter extends Exporter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option == null) {
|
if (addressSpaceOption == null || recordSizeOption == null) {
|
||||||
getOptions(() -> program);
|
getOptions(() -> program);
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintWriter writer = new PrintWriter(new FileOutputStream(file));
|
try (PrintWriter writer = new PrintWriter(new FileOutputStream(file))) {
|
||||||
|
|
||||||
Memory memory = program.getMemory();
|
Memory memory = program.getMemory();
|
||||||
|
|
||||||
if (addrSet == null) {
|
if (addrSet == null) {
|
||||||
addrSet = memory;
|
addrSet = memory;
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
List<IntelHexRecord> records = dumpMemory(program, memory, addrSet, monitor);
|
|
||||||
for (IntelHexRecord record : records) {
|
|
||||||
writer.println(record.format());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (MemoryAccessException e) {
|
|
||||||
throw new ExporterException(e);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Close the PrintWriter
|
|
||||||
//
|
|
||||||
writer.close();
|
|
||||||
|
|
||||||
option = null;
|
try {
|
||||||
|
List<IntelHexRecord> records = dumpMemory(program, memory, addrSet, monitor);
|
||||||
|
for (IntelHexRecord record : records) {
|
||||||
|
writer.println(record.format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MemoryAccessException e) {
|
||||||
|
throw new ExporterException(e);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
addressSpaceOption = null;
|
||||||
|
recordSizeOption = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -118,15 +191,19 @@ public class IntelHexExporter extends Exporter {
|
|||||||
|
|
||||||
protected List<IntelHexRecord> dumpMemory(Program program, Memory memory,
|
protected List<IntelHexRecord> dumpMemory(Program program, Memory memory,
|
||||||
AddressSetView addrSetView, TaskMonitor monitor) throws MemoryAccessException {
|
AddressSetView addrSetView, TaskMonitor monitor) throws MemoryAccessException {
|
||||||
IntelHexRecordWriter writer = new IntelHexRecordWriter(MAX_BYTES_PER_LINE);
|
|
||||||
|
int size = (int) recordSizeOption.getValue();
|
||||||
|
boolean dropBytes = recordSizeOption.dropExtraBytes();
|
||||||
|
|
||||||
|
IntelHexRecordWriter writer = new IntelHexRecordWriter(size, dropBytes);
|
||||||
|
|
||||||
AddressSet set = new AddressSet(addrSetView);
|
AddressSet set = new AddressSet(addrSetView);
|
||||||
|
|
||||||
MemoryBlock[] blocks = memory.getBlocks();
|
MemoryBlock[] blocks = memory.getBlocks();
|
||||||
for (int i = 0; i < blocks.length; ++i) {
|
for (MemoryBlock block : blocks) {
|
||||||
if (!blocks[i].isInitialized() ||
|
if (!block.isInitialized() ||
|
||||||
blocks[i].getStart().getAddressSpace() != option.getValue()) {
|
block.getStart().getAddressSpace() != addressSpaceOption.getValue()) {
|
||||||
set.delete(new AddressRangeImpl(blocks[i].getStart(), blocks[i].getEnd()));
|
set.delete(new AddressRangeImpl(block.getStart(), block.getEnd()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,4 +225,113 @@ public class IntelHexExporter extends Exporter {
|
|||||||
}
|
}
|
||||||
return writer.finish(entryPoint);
|
return writer.finish(entryPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option for exporting Intel Hex records that allows users to specify a record size for the
|
||||||
|
* output. Users may also optionally select the <code>Drop Extra Bytes</code> option that
|
||||||
|
* will cause only those records that match the maximum size to be output to the file.
|
||||||
|
*
|
||||||
|
* @see RecordSizeComponent
|
||||||
|
*/
|
||||||
|
private class RecordSizeOption extends Option {
|
||||||
|
|
||||||
|
private final RecordSizeComponent comp = new RecordSizeComponent(DEFAULT_RECORD_SIZE);
|
||||||
|
|
||||||
|
public RecordSizeOption(String name, Class<?> valueClass) {
|
||||||
|
super(name, valueClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordSizeOption(String name, Class<?> valueClass, Object value, String arg,
|
||||||
|
String group) {
|
||||||
|
super(name, valueClass, value, arg, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getCustomEditorComponent() {
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Option copy() {
|
||||||
|
return new RecordSizeOption(getName(), getValueClass(), getValue(), getArg(),
|
||||||
|
getGroup());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue() {
|
||||||
|
return comp.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getValueClass() {
|
||||||
|
return Integer.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean dropExtraBytes() {
|
||||||
|
return comp.dropExtraBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecordSize(int recordSize) {
|
||||||
|
comp.setRecordSize(recordSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDropBytes(boolean dropBytes) {
|
||||||
|
comp.setDropBytes(dropBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that displays two widgets for setting export options:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li><code>input</code>: a {@link HintTextField} for entering numeric digits; these
|
||||||
|
* represent the record size for each line of output</li>
|
||||||
|
* <li>dropCb: a {@link JCheckBox} for specifying a setting that enforces that every line in
|
||||||
|
* the output matches the specified record size</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* Note: If the <code>Drop Extra Bytes</code> option is set, any bytes that are left over
|
||||||
|
* after outputting all lines that match the record size will be omitted from the output.
|
||||||
|
*/
|
||||||
|
private class RecordSizeComponent extends JPanel {
|
||||||
|
|
||||||
|
private HintTextField input;
|
||||||
|
private JCheckBox dropCb;
|
||||||
|
|
||||||
|
public RecordSizeComponent(int recordSize) {
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
input = new HintTextField(Integer.toString(recordSize), false, new BoundedIntegerVerifier());
|
||||||
|
dropCb = new JCheckBox("Align To Record Size");
|
||||||
|
|
||||||
|
input.setText(Integer.toString(recordSize));
|
||||||
|
|
||||||
|
add(input, BorderLayout.CENTER);
|
||||||
|
add(dropCb, BorderLayout.EAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
String val = input.getText();
|
||||||
|
if (!input.isFieldValid()) {
|
||||||
|
|
||||||
|
// If the user clears the input field, revert to the default
|
||||||
|
// record size (16).
|
||||||
|
return DEFAULT_RECORD_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Integer.valueOf(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean dropExtraBytes() {
|
||||||
|
return dropCb.isSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecordSize(int recordSize) {
|
||||||
|
input.setText(Integer.toString(recordSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDropBytes(boolean dropBytes) {
|
||||||
|
dropCb.setSelected(dropBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,21 +20,31 @@ import java.util.*;
|
|||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
|
||||||
public class IntelHexRecordWriter {
|
public class IntelHexRecordWriter {
|
||||||
|
|
||||||
private final int maxBytesPerLine;
|
private final int maxBytesPerLine;
|
||||||
|
private final boolean dropExtraBytes;
|
||||||
|
|
||||||
private Address startAddress = null;
|
private Address startAddress = null;
|
||||||
private Long oldSegment = null;
|
private Long oldSegment = null;
|
||||||
private ArrayList<Byte> bytes = new ArrayList<Byte>();
|
private ArrayList<Byte> bytes = new ArrayList<>();
|
||||||
private Boolean isSegmented = null;
|
private Boolean isSegmented = null;
|
||||||
|
|
||||||
private ArrayList<IntelHexRecord> results = new ArrayList<IntelHexRecord>();
|
private ArrayList<IntelHexRecord> results = new ArrayList<>();
|
||||||
private boolean done = false;
|
private boolean done = false;
|
||||||
|
|
||||||
public IntelHexRecordWriter(int maxBytesPerLine) {
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param maxBytesPerLine the maximum number of bytes to write per line in the hex output
|
||||||
|
* @param dropExtraBytes if true, only lines matching {@link #maxBytesPerLine} will be output;
|
||||||
|
* remaining bytes will be left out
|
||||||
|
*/
|
||||||
|
public IntelHexRecordWriter(int maxBytesPerLine, boolean dropExtraBytes) {
|
||||||
if (maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH) {
|
if (maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH) {
|
||||||
throw new IllegalArgumentException("maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH");
|
throw new IllegalArgumentException("maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH");
|
||||||
}
|
}
|
||||||
this.maxBytesPerLine = maxBytesPerLine;
|
this.maxBytesPerLine = maxBytesPerLine;
|
||||||
|
this.dropExtraBytes = dropExtraBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addByte(Address address, byte b) {
|
public void addByte(Address address, byte b) {
|
||||||
@ -117,6 +127,14 @@ public class IntelHexRecordWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<IntelHexRecord> finish(Address entryPoint) {
|
public List<IntelHexRecord> finish(Address entryPoint) {
|
||||||
|
|
||||||
|
// Before finalizing things, write out any remaining bytes that haven't yet been written, if
|
||||||
|
// the user has specified to do so via the drop extra bytes option (false =
|
||||||
|
// write out everything).
|
||||||
|
if (bytes.size() > 0 && !dropExtraBytes) {
|
||||||
|
emitData();
|
||||||
|
}
|
||||||
|
|
||||||
if (entryPoint != null && isSegmented != null) {
|
if (entryPoint != null && isSegmented != null) {
|
||||||
final long offset = entryPoint.getOffset();
|
final long offset = entryPoint.getOffset();
|
||||||
byte[] data = new byte[4];
|
byte[] data = new byte[4];
|
||||||
|
Loading…
Reference in New Issue
Block a user