From 3cbd8de918e6ca811526e9706980d2d518c02cd6 Mon Sep 17 00:00:00 2001 From: adamopolous Date: Wed, 27 Mar 2019 14:16:31 -0400 Subject: [PATCH] GT-2703: fixed intel hex export issue of dropping bytes on selection --- .../help/topics/ExporterPlugin/exporter.htm | 6 + .../images/Intel_Hex_Options.png | Bin 6240 -> 8014 bytes .../src/main/java/ghidra/app/util/Option.java | 9 + .../app/util/exporter/IntelHexExporter.java | 220 +++++++++++++++--- .../util/opinion/IntelHexRecordWriter.java | 23 +- 5 files changed, 225 insertions(+), 33 deletions(-) diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ExporterPlugin/exporter.htm b/Ghidra/Features/Base/src/main/help/help/topics/ExporterPlugin/exporter.htm index 27f1bb5d34..2166be79db 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/ExporterPlugin/exporter.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/ExporterPlugin/exporter.htm @@ -306,6 +306,12 @@
  • Address Space - Specifies which address space to export as Intel Hex format only supports one address space. This option will be intialized to the "default" address space.
  • +
  • Record Size - Specifies the size (in bytes) of each record in the + output file. The default 16.
  • +
  • Force - If checked, this will ensure that only 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.
  • diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ExporterPlugin/images/Intel_Hex_Options.png b/Ghidra/Features/Base/src/main/help/help/topics/ExporterPlugin/images/Intel_Hex_Options.png index 31e0df87d2f5b3292dece58a88777de556cd99ec..19ecef3db6d3da860dbce4b104958453a5478351 100644 GIT binary patch literal 8014 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 Tsxt7N{DHE(x?GKnW!V1$hx*SQ literal 6240 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 + * 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() { return null; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/IntelHexExporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/IntelHexExporter.java index 29242fff3b..eaf04de5bc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/IntelHexExporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/IntelHexExporter.java @@ -15,10 +15,15 @@ */ package ghidra.app.util.exporter; +import java.awt.BorderLayout; +import java.awt.Component; import java.io.*; import java.util.ArrayList; import java.util.List; +import javax.swing.*; + +import docking.widgets.textfield.HintTextField; import ghidra.app.util.*; import ghidra.app.util.opinion.IntelHexRecord; import ghidra.app.util.opinion.IntelHexRecordWriter; @@ -29,10 +34,22 @@ import ghidra.program.model.mem.*; import ghidra.util.HelpLocation; import ghidra.util.task.TaskMonitor; +/** + * Exports the current program (or program selection) as bytes in Intel Hex format. + *

    + * 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 force a record size for every line + * of output, which will only print out lines that match the max record size; 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 { - 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; /** * Constructs a new Intel Hex exporter. @@ -41,6 +58,13 @@ public class IntelHexExporter extends Exporter { this("Intel Hex", "hex", new HelpLocation("ExporterPlugin", "intel_hex")); } + /** + * 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) { super(name, extension, help); } @@ -55,16 +79,150 @@ public class IntelHexExporter extends Exporter { } Program program = (Program) domainObject; - option = new Option("Address Space", program.getAddressFactory().getDefaultAddressSpace()); + addressSpaceOption = + new Option("Address Space", program.getAddressFactory().getDefaultAddressSpace()); + + recordSizeOption = new RecordSizeOption("Record Size", Integer.class); + + optionsList.add(addressSpaceOption); + optionsList.add(recordSizeOption); - optionsList.add(option); return optionsList; } @Override public void setOptions(List

      + *
    • input: a {@link HintTextField} for entering numeric digits; these + * represent the record size for each line of output
    • + *
    • forceCb: a {@link JCheckBox} for specifying the force setting; this + * enforces that every line in the output matches the specified record size
    • + *
    + * + * Note: If the force option is set, any bytes that are left over after outputting + * all lines that match the record size will be dropped on the floor. + */ + private class RecordSizeComponent extends JPanel { + + private HintTextField input; + private JCheckBox forceCb; + + public RecordSizeComponent(int recordSize) { + setLayout(new BorderLayout()); + + input = new HintTextField(String.valueOf(recordSize), false, new BoundedIntegerVerifier()); + forceCb = new JCheckBox("force"); + + input.setText(String.valueOf(recordSize)); + + add(input, BorderLayout.CENTER); + add(forceCb, BorderLayout.EAST); + } + + public int getValue() { + String val = input.getText(); + if (!input.isFieldValid()) { + return 0x10; + } + + return Integer.valueOf(val); + } + + public boolean getForce() { + return forceCb.isSelected(); + } + } + + /** + * Verifier for a {@link HintTextField} that ensures input is a numeric value between + * 0 and 0xFF. + *

    + * 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(); + + // Strip off any leading "0x" chars if the user has put them there - the + // parseInt method can't handle them. + if (text.startsWith("0x")) { + text = text.substring(2); + } + + // Now try and parse the input; first as hex, then as decimal. + Integer val; + try { + val = Integer.parseInt(text, 16); + } + catch (NumberFormatException e) { + try { + val = Integer.parseInt(text); + } + catch (NumberFormatException e1) { + return false; + } + } + + return val <= 0xFF && val >= 0; } } @@ -84,33 +242,31 @@ public class IntelHexExporter extends Exporter { return false; } - if (option == null) { + if (addressSpaceOption == null || recordSizeOption == null) { 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) { - addrSet = memory; - } - - try { - List records = dumpMemory(program, memory, addrSet, monitor); - for (IntelHexRecord record : records) { - writer.println(record.format()); + if (addrSet == null) { + addrSet = memory; } - } - catch (MemoryAccessException e) { - throw new ExporterException(e); - } - finally { - // Close the PrintWriter - // - writer.close(); - option = null; + try { + List 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; @@ -118,15 +274,19 @@ public class IntelHexExporter extends Exporter { protected List dumpMemory(Program program, Memory memory, AddressSetView addrSetView, TaskMonitor monitor) throws MemoryAccessException { - IntelHexRecordWriter writer = new IntelHexRecordWriter(MAX_BYTES_PER_LINE); + + int size = (int) recordSizeOption.getValue(); + boolean forceSize = recordSizeOption.getForce(); + + IntelHexRecordWriter writer = new IntelHexRecordWriter(size, forceSize); AddressSet set = new AddressSet(addrSetView); MemoryBlock[] blocks = memory.getBlocks(); - for (int i = 0; i < blocks.length; ++i) { - if (!blocks[i].isInitialized() || - blocks[i].getStart().getAddressSpace() != option.getValue()) { - set.delete(new AddressRangeImpl(blocks[i].getStart(), blocks[i].getEnd())); + for (MemoryBlock block : blocks) { + if (!block.isInitialized() || + block.getStart().getAddressSpace() != addressSpaceOption.getValue()) { + set.delete(new AddressRangeImpl(block.getStart(), block.getEnd())); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/IntelHexRecordWriter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/IntelHexRecordWriter.java index 623417a12a..471842790e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/IntelHexRecordWriter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/IntelHexRecordWriter.java @@ -20,21 +20,31 @@ import java.util.*; import ghidra.program.model.address.*; public class IntelHexRecordWriter { + private final int maxBytesPerLine; + private final boolean forceSize; private Address startAddress = null; private Long oldSegment = null; - private ArrayList bytes = new ArrayList(); + private ArrayList bytes = new ArrayList<>(); private Boolean isSegmented = null; - private ArrayList results = new ArrayList(); + private ArrayList results = new ArrayList<>(); 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 forceSize if true, only lines matching {@link #maxBytesPerLine} will be output; + * remaining bytes will be left out + */ + public IntelHexRecordWriter(int maxBytesPerLine, boolean forceSize) { if (maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH) { throw new IllegalArgumentException("maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH"); } this.maxBytesPerLine = maxBytesPerLine; + this.forceSize = forceSize; } public void addByte(Address address, byte b) { @@ -117,6 +127,13 @@ public class IntelHexRecordWriter { } public List 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 force option (false = write out everything). + if (bytes.size() > 0 && !forceSize) { + emitData(); + } + if (entryPoint != null && isSegmented != null) { final long offset = entryPoint.getOffset(); byte[] data = new byte[4];