From d58923419ce4d7dbea05810b39c92ab4d3c6b5be Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Fri, 28 Jun 2024 08:53:05 -0400 Subject: [PATCH] GP-4729: More OmfLoader refactoring. Initial Omf51Loader framework. --- .../format/omf/AbstractOmfRecordFactory.java | 75 ++++++++++++ .../bin/format/omf/OmfObsoleteRecord.java | 8 +- .../app/util/bin/format/omf/OmfRecord.java | 72 ++++++++---- .../app/util/bin/format/omf/OmfString.java | 5 + .../util/bin/format/omf/OmfUnknownRecord.java | 8 +- .../bin/format/omf/OmfUnsupportedRecord.java | 8 +- .../app/util/bin/format/omf/OmfUtils.java | 27 +++++ .../omf/omf/OmfComdatExternalSymbol.java | 19 ++- .../bin/format/omf/omf/OmfComdefRecord.java | 24 ++-- .../bin/format/omf/omf/OmfCommentRecord.java | 16 +-- .../app/util/bin/format/omf/omf/OmfData.java | 4 + .../bin/format/omf/omf/OmfEnumeratedData.java | 17 +-- .../bin/format/omf/omf/OmfExternalSymbol.java | 18 ++- .../bin/format/omf/omf/OmfFileHeader.java | 40 +++---- .../bin/format/omf/omf/OmfFixupRecord.java | 16 +-- .../bin/format/omf/omf/OmfGroupRecord.java | 16 +-- .../bin/format/omf/omf/OmfIteratedData.java | 21 ++-- .../bin/format/omf/omf/OmfLibraryRecord.java | 28 ++--- .../util/bin/format/omf/omf/OmfModuleEnd.java | 28 +---- .../bin/format/omf/omf/OmfNamesRecord.java | 12 +- .../bin/format/omf/omf/OmfRecordFactory.java | 34 +++++- .../bin/format/omf/omf/OmfRecordTypes.java | 6 + .../bin/format/omf/omf/OmfSegmentHeader.java | 33 +++--- .../bin/format/omf/omf/OmfSymbolRecord.java | 23 ++-- .../bin/format/omf/omf166/Omf166DepList.java | 81 +++++++++++++ .../format/omf/omf166/Omf166RecordTypes.java | 73 ++++++++++++ .../bin/format/omf/omf51/Omf51ModuleEnd.java | 70 +++++++++++ .../format/omf/omf51/Omf51ModuleHeader.java | 67 +++++++++++ .../format/omf/omf51/Omf51RecordFactory.java | 83 +++++++++++++ .../format/omf/omf51/Omf51RecordTypes.java | 50 ++++++++ .../ghidra/app/util/opinion/Omf51Loader.java | 111 ++++++++++++++++++ .../ghidra/app/util/opinion/OmfLoader.java | 44 +++---- .../formats/omf/OmfArchiveFileSystem.java | 12 +- .../omf/OmfArchiveFileSystemFactory.java | 16 ++- Ghidra/Processors/8051/certification.manifest | 1 + .../8051/data/languages/8051.opinion | 7 ++ 36 files changed, 940 insertions(+), 233 deletions(-) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/AbstractOmfRecordFactory.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf166/Omf166DepList.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf166/Omf166RecordTypes.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51ModuleEnd.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51ModuleHeader.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordFactory.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordTypes.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/Omf51Loader.java create mode 100644 Ghidra/Processors/8051/data/languages/8051.opinion diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/AbstractOmfRecordFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/AbstractOmfRecordFactory.java new file mode 100644 index 0000000000..e5cb912600 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/AbstractOmfRecordFactory.java @@ -0,0 +1,75 @@ +/* ### + * 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.bin.format.omf; + +import java.io.IOException; +import java.util.List; + +import ghidra.app.util.bin.BinaryReader; + +/** + * Classes that implement this interface can read various flavors of the OMF format + */ +public abstract class AbstractOmfRecordFactory { + + protected BinaryReader reader; + + /** + * Creates a new {@link AbstractOmfRecordFactory} + * + * @param reader The {@link BinaryReader} used to read records + */ + protected AbstractOmfRecordFactory(BinaryReader reader) { + this.reader = reader; + } + + /** + * Reads the next {@link OmfRecord} pointed to by the reader + * + * @return The next read {@link OmfRecord} + * @throws IOException if there was an IO-related error + * @throws OmfException if there was a problem with the OMF specification + */ + public abstract OmfRecord readNextRecord() throws IOException, OmfException; + + /** + * Gets a {@link List} of valid record types that can start a supported OMF binary + * + * @return A {@link List} of valid record types that can start a supported OMF binary + */ + public abstract List getStartRecordTypes(); + + /** + * Gets a valid record type that can end a supported OMF binary + * + * @return A valid record types that can end a supported OMF binary + */ + public abstract int getEndRecordType(); + + /** + * {@return the reader associated with this factory} + */ + public BinaryReader getReader() { + return reader; + } + + /** + * Reset this factory's reader to index 0 + */ + public void reset() { + reader.setPointerIndex(0); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfObsoleteRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfObsoleteRecord.java index 4cc406da83..d3f9698b0c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfObsoleteRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfObsoleteRecord.java @@ -31,8 +31,12 @@ public class OmfObsoleteRecord extends OmfRecord { * @throws IOException If an IO-related error occurred */ public OmfObsoleteRecord(BinaryReader reader) throws IOException { - readRecordHeader(reader); - reader.setPointerIndex(reader.getPointerIndex() + getRecordLength()); + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + // No record-specific data to read } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfRecord.java index 5dbc5d5547..1eb7fc45d1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfRecord.java @@ -29,31 +29,48 @@ public abstract class OmfRecord implements StructConverter { protected int recordType; protected int recordLength; + protected byte[] data; protected byte checkSum; protected long recordOffset; + protected BinaryReader dataReader; + protected long dataEnd; /** - * Reads the record header (type and length fields) + * + */ + public OmfRecord() { + // nothing to do + } + + /** + * Creates a new {@link OmfRecord} * * @param reader A {@link BinaryReader} positioned at the start of the record - * @throws IOException if an IO-related problem occurred + * @throws IOException if there was an IO-related error */ - public void readRecordHeader(BinaryReader reader) throws IOException { - recordOffset = reader.getPointerIndex(); - recordType = reader.readNextUnsignedByte(); - recordLength = reader.readNextUnsignedShort(); + public OmfRecord(BinaryReader reader) throws IOException { + this.recordOffset = reader.getPointerIndex(); + + this.recordType = reader.readNextUnsignedByte(); + this.recordLength = reader.readNextUnsignedShort(); + this.data = reader.readNextByteArray(recordLength - 1); + this.checkSum = reader.readNextByte(); + + this.dataReader = reader.clone(recordOffset + 3); + this.dataEnd = recordOffset + 3 + recordLength - 1; } /** - * Reads the record checksum + * Parses this {@link OmfRecord}'s type-spefic data * - * @param reader A {@link BinaryReader} positioned at the start of the record checksum - * @throws IOException if an IO-related problem occurred + * @throws IOException if there was an IO-related error + * @throws OmfException if there was a problem with the OMF specification */ - public void readCheckSumByte(BinaryReader reader) throws IOException { - checkSum = reader.readNextByte(); - } + public abstract void parseData() throws IOException, OmfException; + + @Override + public abstract DataType toDataType() throws DuplicateNameException, IOException; /** * {@return the record type} @@ -76,36 +93,42 @@ public abstract class OmfRecord implements StructConverter { return recordOffset; } + public byte getRecordChecksum() { + return checkSum; + } + + public byte[] getData() { + return data; + } + /** * Computes the record's checksum * - * @param reader A {@link BinaryReader} positioned at the start of record * @return The record's checksum * @throws IOException if an IO-related error occurred */ - public byte calcCheckSum(BinaryReader reader) throws IOException { - byte res = reader.readNextByte(); - res += reader.readNextByte(); - res += reader.readNextByte(); // Sum the record header bytes - for (int i = 0; i < recordLength; ++i) { - res += reader.readNextByte(); + public byte calcCheckSum() throws IOException { + byte sum = (byte) recordType; + sum += (byte) recordLength + (byte) (recordLength >> 8); + for (byte b : data) { + sum += b; } - return res; + sum += checkSum; + return sum; } /** * Validates the record's checksum * - * @param reader A {@link BinaryReader} positioned at the start of the record * @return True if the checksum is valid; otherwise, false * @throws IOException if an IO-related error occurred */ - public boolean validCheckSum(BinaryReader reader) throws IOException { + public boolean validCheckSum() throws IOException { if (checkSum == 0) { // Some compilers just set this to zero return true; } - return (calcCheckSum(reader) == 0); + return (calcCheckSum() == 0); } /** @@ -115,9 +138,6 @@ public abstract class OmfRecord implements StructConverter { return ((recordType & 1) != 0); } - @Override - public abstract DataType toDataType() throws DuplicateNameException, IOException; - @Override public String toString() { return String.format("type: 0x%x, offset: 0x%x, length: 0x%x", recordType, recordOffset, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfString.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfString.java index c571d71c19..236b09eccf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfString.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfString.java @@ -54,6 +54,11 @@ public class OmfString implements StructConverter { return str; } + @Override + public String toString() { + return str; + } + @Override public DataType toDataType() throws DuplicateNameException, IOException { if (length == 0) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUnknownRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUnknownRecord.java index 7c018aca55..57c9f6040e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUnknownRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUnknownRecord.java @@ -34,8 +34,12 @@ public class OmfUnknownRecord extends OmfRecord { * @throws IOException If an IO-related error occurred */ public OmfUnknownRecord(BinaryReader reader) throws IOException { - readRecordHeader(reader); - reader.setPointerIndex(reader.getPointerIndex() + getRecordLength()); + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + // No record-specific data to read } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUnsupportedRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUnsupportedRecord.java index 276b223645..85481cfd79 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUnsupportedRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUnsupportedRecord.java @@ -36,9 +36,13 @@ public class OmfUnsupportedRecord extends OmfRecord { * @throws IOException If an IO-related error occurred */ public OmfUnsupportedRecord(BinaryReader reader, Class recordTypesClass) throws IOException { + super(reader); this.recordTypesClass = recordTypesClass; - readRecordHeader(reader); - reader.setPointerIndex(reader.getPointerIndex() + getRecordLength()); + } + + @Override + public void parseData() throws IOException, OmfException { + // No record-specific data to read } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUtils.java index 9765530e50..df55edc614 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfUtils.java @@ -18,6 +18,8 @@ package ghidra.app.util.bin.format.omf; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; import ghidra.app.util.bin.BinaryReader; import ghidra.program.model.data.*; @@ -105,4 +107,29 @@ public class OmfUtils { struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); return struct; } + + /** + * Reads all the {@link OmfRecord records} associated with the given + * {@link AbstractOmfRecordFactory} + * + * @param factory The {@link AbstractOmfRecordFactory} + * @return A {@link List} of read {@link OmfRecord records} + * @throws IOException if there was an IO-related error + * @throws OmfException if there was a problem with the OMF specification + */ + public static List readRecords(AbstractOmfRecordFactory factory) + throws OmfException, IOException { + List records = new ArrayList<>(); + factory.reset(); + + while (true) { + OmfRecord rec = factory.readNextRecord(); + records.add(rec); + if (rec.getRecordType() == factory.getEndRecordType()) { + break; + } + } + + return records; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfComdatExternalSymbol.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfComdatExternalSymbol.java index d4b35c63b8..9090283020 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfComdatExternalSymbol.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfComdatExternalSymbol.java @@ -20,8 +20,7 @@ import java.util.ArrayList; import java.util.List; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.omf.OmfIndex; -import ghidra.app.util.bin.format.omf.OmfUtils; +import ghidra.app.util.bin.format.omf.*; public class OmfComdatExternalSymbol extends OmfExternalSymbol { @@ -29,17 +28,17 @@ public class OmfComdatExternalSymbol extends OmfExternalSymbol { protected List externalLookups = new ArrayList<>(); public OmfComdatExternalSymbol(BinaryReader reader) throws IOException { - super(false); - readRecordHeader(reader); + super(reader, false); - long max = reader.getPointerIndex() + getRecordLength() - 1; - while (reader.getPointerIndex() < max) { - OmfIndex nameIndex = OmfUtils.readIndex(reader); - OmfIndex type = OmfUtils.readIndex(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + while (dataReader.getPointerIndex() < dataEnd) { + OmfIndex nameIndex = OmfUtils.readIndex(dataReader); + OmfIndex type = OmfUtils.readIndex(dataReader); externalLookups.add(new ExternalLookup(nameIndex.value(), type.value())); } - - readCheckSumByte(reader); } public void loadNames(List nameList) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfComdefRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfComdefRecord.java index f9c9be5627..f7b0367296 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfComdefRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfComdefRecord.java @@ -22,28 +22,28 @@ import ghidra.app.util.bin.format.omf.*; public class OmfComdefRecord extends OmfExternalSymbol { - public OmfComdefRecord(BinaryReader reader, boolean isStatic) throws IOException, OmfException { - super(isStatic); - readRecordHeader(reader); + public OmfComdefRecord(BinaryReader reader, boolean isStatic) throws IOException { + super(reader, isStatic); + } - long max = reader.getPointerIndex() + getRecordLength() - 1; - while (reader.getPointerIndex() < max) { - OmfString name = OmfUtils.readString(reader); - OmfIndex typeIndex = OmfUtils.readIndex(reader); - byte dataType = reader.readNextByte(); + @Override + public void parseData() throws IOException, OmfException { + while (dataReader.getPointerIndex() < dataEnd) { + OmfString name = OmfUtils.readString(dataReader); + OmfIndex typeIndex = OmfUtils.readIndex(dataReader); + byte dataType = dataReader.readNextByte(); int byteLength = 0; if (dataType == 0x61) { // FAR data, reads numElements and elSize - int numElements = readCommunalLength(reader); - int elSize = readCommunalLength(reader); + int numElements = readCommunalLength(dataReader); + int elSize = readCommunalLength(dataReader); byteLength = numElements * elSize; } else { // Values 1 thru 5f plus 61, read the byte length - byteLength = readCommunalLength(reader); + byteLength = readCommunalLength(dataReader); } symbols.add(new OmfSymbol(name.str(), typeIndex.value(), 0, dataType, byteLength)); } - readCheckSumByte(reader); } private static int readCommunalLength(BinaryReader reader) throws OmfException, IOException { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfCommentRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfCommentRecord.java index 74bff9618f..b580112b82 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfCommentRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfCommentRecord.java @@ -40,25 +40,27 @@ public class OmfCommentRecord extends OmfRecord { private OmfString value; public OmfCommentRecord(BinaryReader reader) throws IOException { - readRecordHeader(reader); - commentType = reader.readNextByte(); - commentClass = reader.readNextByte(); + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + commentType = dataReader.readNextByte(); + commentClass = dataReader.readNextByte(); switch (commentClass) { case COMMENT_CLASS_TRANSLATOR: case COMMENT_CLASS_DEFAULT_LIBRARY: - byte[] bytes = reader.readNextByteArray(getRecordLength() - + byte[] bytes = dataReader.readNextByteArray(getRecordLength() - 3 /* 3 = sizeof(commentType+commentClass+trailing_crcbyte*/); value = new OmfString(bytes.length, new String(bytes, StandardCharsets.US_ASCII)); // assuming ASCII break; case COMMENT_CLASS_LIBMOD: - value = OmfUtils.readString(reader); + value = OmfUtils.readString(dataReader); break; default: - reader.setPointerIndex(reader.getPointerIndex() + getRecordLength() - 3); break; } - readCheckSumByte(reader); } public byte getCommentType() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfData.java index 5bd4b6fc1a..5088161ddc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfData.java @@ -27,6 +27,10 @@ public abstract class OmfData extends OmfRecord implements Comparable { protected OmfIndex segmentIndex; protected Omf2or4 dataOffset; + public OmfData(BinaryReader reader) throws IOException { + super(reader); + } + /** * @return get the segments index for this datablock */ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfEnumeratedData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfEnumeratedData.java index acd47b5fd5..8c38529628 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfEnumeratedData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfEnumeratedData.java @@ -18,6 +18,7 @@ package ghidra.app.util.bin.format.omf.omf; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.omf.OmfException; import ghidra.app.util.bin.format.omf.OmfUtils; import ghidra.program.model.data.DataType; import ghidra.util.exception.DuplicateNameException; @@ -27,14 +28,16 @@ public class OmfEnumeratedData extends OmfData { private int streamLength; // Number of bytes of data public OmfEnumeratedData(BinaryReader reader) throws IOException { - readRecordHeader(reader); - long start = reader.getPointerIndex(); - segmentIndex = OmfUtils.readIndex(reader); - dataOffset = OmfUtils.readInt2Or4(reader, hasBigFields()); - streamOffset = reader.getPointerIndex(); + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + long start = dataReader.getPointerIndex(); + segmentIndex = OmfUtils.readIndex(dataReader); + dataOffset = OmfUtils.readInt2Or4(dataReader, hasBigFields()); + streamOffset = dataReader.getPointerIndex(); streamLength = getRecordLength() - 1 - (int) (streamOffset - start); - reader.setPointerIndex(streamOffset + streamLength); // Skip over the data when reading header - readCheckSumByte(reader); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfExternalSymbol.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfExternalSymbol.java index 06e13e25e2..08102f6ae5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfExternalSymbol.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfExternalSymbol.java @@ -33,23 +33,19 @@ public class OmfExternalSymbol extends OmfRecord { private List refs = new ArrayList<>(); - protected OmfExternalSymbol(boolean isStatic) { + public OmfExternalSymbol(BinaryReader reader, boolean isStatic) throws IOException { + super(reader); this.isStatic = isStatic; } - public OmfExternalSymbol(BinaryReader reader, boolean isStatic) throws IOException { - this.isStatic = isStatic; - readRecordHeader(reader); - - long max = reader.getPointerIndex() + getRecordLength() - 1; - while (reader.getPointerIndex() < max) { - OmfString name = OmfUtils.readString(reader); - OmfIndex type = OmfUtils.readIndex(reader); + @Override + public void parseData() throws IOException, OmfException { + while (dataReader.getPointerIndex() < dataEnd) { + OmfString name = OmfUtils.readString(dataReader); + OmfIndex type = OmfUtils.readIndex(dataReader); refs.add(new Reference(name, type)); symbols.add(new OmfSymbol(name.str(), type.value(), 0, 0, 0)); } - - readCheckSumByte(reader); } public List getSymbols() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfFileHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfFileHeader.java index ecfb1af3fb..efbdef641d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfFileHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfFileHeader.java @@ -20,7 +20,6 @@ import java.util.ArrayList; import java.util.List; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.format.omf.*; import ghidra.app.util.importer.MessageLog; import ghidra.program.model.data.*; @@ -45,12 +44,15 @@ public class OmfFileHeader extends OmfRecord { private boolean format16bit; public OmfFileHeader(BinaryReader reader) throws IOException { - readRecordHeader(reader); - objectName = OmfUtils.readString(reader); // This is usually the source code filename - readCheckSumByte(reader); + super(reader); isLittleEndian = reader.isLittleEndian(); } + @Override + public void parseData() throws IOException, OmfException { + objectName = OmfUtils.readString(dataReader); // This is usually the source code filename + } + /** * {@return the list of records} */ @@ -261,23 +263,23 @@ public class OmfFileHeader extends OmfRecord { /** * Scan the object file, for the main header and comment records. Other records are parsed but not saved - * @param reader is the byte stream + * @param factory the {@link AbstractOmfRecordFactory} * @param monitor is checked for cancellation * @param fastscan is true if we only want to scan the header until first seghead, * @return the header record * @throws IOException for problems reading program data * @throws OmfException for malformed records */ - public static OmfFileHeader scan(BinaryReader reader, TaskMonitor monitor, boolean fastscan) - throws IOException, OmfException { - OmfRecord record = OmfRecordFactory.readRecord(reader); + public static OmfFileHeader scan(AbstractOmfRecordFactory factory, TaskMonitor monitor, + boolean fastscan) throws IOException, OmfException { + OmfRecord record = factory.readNextRecord(); if (!(record instanceof OmfFileHeader)) { throw new OmfException("Object file does not start with proper header"); } OmfFileHeader header = (OmfFileHeader) record; while (true) { - record = OmfRecordFactory.readRecord(reader); + record = factory.readNextRecord(); if (monitor.isCancelled()) { break; @@ -315,16 +317,16 @@ public class OmfFileHeader extends OmfRecord { /** * Parse the entire object file * - * @param reader is the byte stream + * @param factory the {@link AbstractOmfRecordFactory} * @param monitor is checked for cancel button * @param log the log * @return the header record as root of object * @throws IOException for problems reading data * @throws OmfException for malformed records */ - public static OmfFileHeader parse(BinaryReader reader, TaskMonitor monitor, MessageLog log) + public static OmfFileHeader parse(AbstractOmfRecordFactory factory, TaskMonitor monitor, MessageLog log) throws IOException, OmfException { - OmfRecord record = OmfRecordFactory.readRecord(reader); + OmfRecord record = factory.readNextRecord(); if (!(record instanceof OmfFileHeader header)) { throw new OmfException("Object file does not start with proper header"); } @@ -332,7 +334,10 @@ public class OmfFileHeader extends OmfRecord { OmfData lastDataBlock = null; while (true) { - record = OmfRecordFactory.readRecord(reader); + record = factory.readNextRecord(); + if (!record.validCheckSum()) { + throw new OmfException("Invalid checksum!"); + } header.records.add(record); if (monitor.isCancelled()) { @@ -488,15 +493,6 @@ public class OmfFileHeader extends OmfRecord { return len == stringlen + 2; } - /** - * Create a reader for a specific OMF file - * @param provider is the underlying ByteProvider - * @return the new reader - */ - public static BinaryReader createReader(ByteProvider provider) { - return new BinaryReader(provider, true/* Always little endian */); - } - private static void logRecord(String description, OmfRecord record, MessageLog log) { log.appendMsg(description + " (" + record + ")"); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfFixupRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfFixupRecord.java index 87052673c3..000799ea54 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfFixupRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfFixupRecord.java @@ -24,7 +24,7 @@ import ghidra.program.model.data.DataType; import ghidra.util.exception.DuplicateNameException; public class OmfFixupRecord extends OmfRecord { - private final Subrecord[] subrecs; + private Subrecord[] subrecs; private OmfData lastData = null; /** @@ -33,16 +33,18 @@ public class OmfFixupRecord extends OmfRecord { * @throws IOException if there was an IO-related error */ public OmfFixupRecord(BinaryReader reader) throws IOException { - ArrayList subreclist = new ArrayList(); + super(reader); + } - readRecordHeader(reader); - long max = reader.getPointerIndex() + getRecordLength() - 1; - while (reader.getPointerIndex() < max) { - subreclist.add(Subrecord.readSubrecord(reader, hasBigFields())); + @Override + public void parseData() throws IOException, OmfException { + ArrayList subreclist = new ArrayList<>(); + long max = dataReader.getPointerIndex() + getRecordLength() - 1; + while (dataReader.getPointerIndex() < max) { + subreclist.add(Subrecord.readSubrecord(dataReader, hasBigFields())); } subrecs = new Subrecord[subreclist.size()]; subreclist.toArray(subrecs); - readCheckSumByte(reader); } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfGroupRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfGroupRecord.java index 9493292c87..791f6c5723 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfGroupRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfGroupRecord.java @@ -34,15 +34,17 @@ public class OmfGroupRecord extends OmfRecord { private GroupSubrecord[] group; public OmfGroupRecord(BinaryReader reader) throws IOException { - readRecordHeader(reader); - long max = reader.getPointerIndex() + getRecordLength() - 1; - groupNameIndex = OmfUtils.readIndex(reader); - ArrayList grouplist = new ArrayList(); - while (reader.getPointerIndex() < max) { - GroupSubrecord subrec = GroupSubrecord.read(reader); + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + groupNameIndex = OmfUtils.readIndex(dataReader); + ArrayList grouplist = new ArrayList<>(); + while (dataReader.getPointerIndex() < dataEnd) { + GroupSubrecord subrec = GroupSubrecord.read(dataReader); grouplist.add(subrec); } - readCheckSumByte(reader); group = new GroupSubrecord[grouplist.size()]; grouplist.toArray(group); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfIteratedData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfIteratedData.java index 19b0f2948b..f6a221ea9c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfIteratedData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfIteratedData.java @@ -19,8 +19,7 @@ import java.io.IOException; import java.util.ArrayList; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.omf.Omf2or4; -import ghidra.app.util.bin.format.omf.OmfUtils; +import ghidra.app.util.bin.format.omf.*; import ghidra.program.model.data.DataType; import ghidra.util.exception.DuplicateNameException; @@ -30,17 +29,19 @@ public class OmfIteratedData extends OmfData { private DataBlock[] datablock; public OmfIteratedData(BinaryReader reader) throws IOException { - readRecordHeader(reader); - long max = reader.getPointerIndex() + getRecordLength() - 1; + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { boolean hasBigFields = hasBigFields(); - segmentIndex = OmfUtils.readIndex(reader); - dataOffset = OmfUtils.readInt2Or4(reader, hasBigFields); - ArrayList blocklist = new ArrayList(); - while (reader.getPointerIndex() < max) { - DataBlock block = DataBlock.read(reader, hasBigFields); + segmentIndex = OmfUtils.readIndex(dataReader); + dataOffset = OmfUtils.readInt2Or4(dataReader, hasBigFields); + ArrayList blocklist = new ArrayList<>(); + while (dataReader.getPointerIndex() < dataEnd) { + DataBlock block = DataBlock.read(dataReader, hasBigFields); blocklist.add(block); } - readCheckSumByte(reader); datablock = new DataBlock[blocklist.size()]; blocklist.toArray(datablock); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfLibraryRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfLibraryRecord.java index 856ca6ca11..35ea7f794d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfLibraryRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfLibraryRecord.java @@ -19,13 +19,14 @@ import java.io.IOException; import java.util.ArrayList; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.omf.*; -import ghidra.program.model.data.DataType; -import ghidra.util.exception.DuplicateNameException; +import ghidra.app.util.bin.format.omf.AbstractOmfRecordFactory; +import ghidra.app.util.bin.format.omf.OmfException; import ghidra.util.task.TaskMonitor; @SuppressWarnings("unused") -public class OmfLibraryRecord extends OmfRecord { +public class OmfLibraryRecord { + private int recordType; + private int recordLength; private int pageSize; // All archive members must start on a page boundary of this size private long dictionaryOffset; private int dictionarySize; @@ -40,13 +41,14 @@ public class OmfLibraryRecord extends OmfRecord { public String machineName; } - public OmfLibraryRecord(BinaryReader reader) throws IOException { - readRecordHeader(reader); + public OmfLibraryRecord(BinaryReader reader) throws IOException, OmfException { + recordType = reader.readNextUnsignedByte(); + recordLength = reader.readNextUnsignedShort(); pageSize = recordLength + 3; dictionaryOffset = reader.readNextInt() & 0xffffffff; dictionarySize = reader.readNextShort() & 0xffff; flags = reader.readNextByte(); - // No checksum byte (just padding) + // No checksum byte } public int getPageSize() { @@ -88,9 +90,10 @@ public class OmfLibraryRecord extends OmfRecord { return true; } - public static OmfLibraryRecord parse(BinaryReader reader, TaskMonitor monitor) - throws IOException { + public static OmfLibraryRecord parse(AbstractOmfRecordFactory factory, TaskMonitor monitor) + throws IOException, OmfException { OmfLibraryRecord res = null; + BinaryReader reader = factory.getReader(); byte type = reader.peekNextByte(); if (type != (byte) 0xF0) { throw new IOException("Not an OMF Library record"); @@ -104,7 +107,7 @@ public class OmfLibraryRecord extends OmfRecord { curheader.payloadOffset = reader.getPointerIndex(); OmfFileHeader fileheader; try { - fileheader = OmfFileHeader.scan(reader, monitor, false); + fileheader = OmfFileHeader.scan(factory, monitor, false); } catch (OmfException e) { throw new IOException("Could not parse individual object file within archive"); @@ -122,9 +125,4 @@ public class OmfLibraryRecord extends OmfRecord { } return res; } - - @Override - public DataType toDataType() throws DuplicateNameException, IOException { - return OmfUtils.toOmfRecordDataType(this, OmfRecordTypes.getName(recordType)); - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfModuleEnd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfModuleEnd.java index a7872f1ed6..8b3b1a040b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfModuleEnd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfModuleEnd.java @@ -18,38 +18,20 @@ package ghidra.app.util.bin.format.omf.omf; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.omf.OmfRecord; -import ghidra.app.util.bin.format.omf.OmfUtils; +import ghidra.app.util.bin.format.omf.*; import ghidra.program.model.data.DataType; import ghidra.util.exception.DuplicateNameException; public class OmfModuleEnd extends OmfRecord { - //private byte moduleType; - //private OmfFixupRecord.FixupTarget startAddress; public OmfModuleEnd(BinaryReader reader) throws IOException { - readRecordHeader(reader); - /* The record type is not handled so simply skip the information - moduleType = reader.readNextByte(); - if (hasStartAddress()) { - endData = reader.readNextByte(); - frameDatum = readInt1Or2(reader, hasBigFields()); - targetDatum = readInt1Or2(reader, hasBigFields()); - targetDisplacement readInt2Or4(reader, hasBigFields()); - } - readCheckSumByte(reader); - */ - reader.setPointerIndex(reader.getPointerIndex() + getRecordLength()); - } -/* - public boolean isMainProgramModule() { - return ((moduleType & 0x80) != 0); + super(reader); } - public boolean hasStartAddress() { - return ((moduleType & 0x40) != 0); + @Override + public void parseData() throws IOException, OmfException { + // No record-specific data to read } -*/ @Override public DataType toDataType() throws DuplicateNameException, IOException { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfNamesRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfNamesRecord.java index 135baf9533..febd0c91f6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfNamesRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfNamesRecord.java @@ -28,12 +28,14 @@ public class OmfNamesRecord extends OmfRecord { private List names = new ArrayList<>(); public OmfNamesRecord(BinaryReader reader) throws IOException { - readRecordHeader(reader); - long max = reader.getPointerIndex() + getRecordLength() - 1; - while (reader.getPointerIndex() < max) { - names.add(OmfUtils.readString(reader)); + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + while (dataReader.getPointerIndex() < dataEnd) { + names.add(OmfUtils.readString(dataReader)); } - readCheckSumByte(reader); } public void appendNames(List namelist) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfRecordFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfRecordFactory.java index b7c219546c..83d2a9c21d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfRecordFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfRecordFactory.java @@ -18,15 +18,30 @@ package ghidra.app.util.bin.format.omf.omf; import static ghidra.app.util.bin.format.omf.omf.OmfRecordTypes.*; import java.io.IOException; +import java.util.List; import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.format.omf.*; -public class OmfRecordFactory { +/** + * A class for reading/creating Relocatable OMF records + */ +public class OmfRecordFactory extends AbstractOmfRecordFactory { - public static OmfRecord readRecord(BinaryReader reader) throws IOException, OmfException { + /** + * Creates a new {@link OmfRecordFactory} + * + * @param provider The {@link ByteProvider} that contains the records + */ + public OmfRecordFactory(ByteProvider provider) { + super(new BinaryReader(provider, true)); + } + + @Override + public OmfRecord readNextRecord() throws IOException, OmfException { int type = Byte.toUnsignedInt(reader.peekNextByte()); - return switch (type & 0xfffffffe) { // mask off the least significant bit (16/32 bit flag) + OmfRecord record = switch (type & 0xfffffffe) { // mask off the least significant bit (16/32 bit flag) case THEADR: case LHEADR: yield new OmfFileHeader(reader); @@ -91,5 +106,18 @@ public class OmfRecordFactory { default: yield new OmfUnknownRecord(reader); }; + + record.parseData(); + return record; + } + + @Override + public List getStartRecordTypes() { + return List.of(THEADR, LHEADR); + } + + @Override + public int getEndRecordType() { + return MODEND; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfRecordTypes.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfRecordTypes.java index f2403e7804..c1f6a47dcb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfRecordTypes.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfRecordTypes.java @@ -17,6 +17,11 @@ package ghidra.app.util.bin.format.omf.omf; import ghidra.app.util.bin.format.omf.OmfUtils; +/** + * Relocatable OMF record types + * + * @see OMF: Relocatable Object Module Format + */ public class OmfRecordTypes { public final static int RHEADR = 0x6E; // Obsolete @@ -62,6 +67,7 @@ public class OmfRecordTypes { public final static int LLNAMES = 0xCA; public final static int VERNUM = 0xCC; public final static int VENDEXT = 0xCE; + public final static int START = 0xF0; public final static int END = 0xF1; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfSegmentHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfSegmentHeader.java index 7f6e30f5e3..dedc0cc70f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfSegmentHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfSegmentHeader.java @@ -83,20 +83,23 @@ public class OmfSegmentHeader extends OmfRecord { } public OmfSegmentHeader(BinaryReader reader) throws IOException { - readRecordHeader(reader); + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { boolean hasBigFields = hasBigFields(); - segAttr = reader.readNextByte(); + segAttr = dataReader.readNextByte(); int A = (segAttr >> 5) & 7; if (A == 0) { - frameNumber = reader.readNextShort() & 0xffff; - offset = reader.readNextByte() & 0xff; + frameNumber = dataReader.readNextShort() & 0xffff; + offset = dataReader.readNextByte() & 0xff; vma = (long) frameNumber + offset; } - segmentLength = OmfUtils.readInt2Or4(reader, hasBigFields); - segmentNameIndex = OmfUtils.readIndex(reader); - classNameIndex = OmfUtils.readIndex(reader); - overlayNameIndex = OmfUtils.readIndex(reader); - readCheckSumByte(reader); + segmentLength = OmfUtils.readInt2Or4(dataReader, hasBigFields); + segmentNameIndex = OmfUtils.readIndex(dataReader); + classNameIndex = OmfUtils.readIndex(dataReader); + overlayNameIndex = OmfUtils.readIndex(dataReader); int B = (segAttr >> 1) & 1; if (B == 1) { // Ignore the segmentLength field if (getRecordType() == OmfRecordTypes.SEGDEF) { @@ -393,10 +396,10 @@ public class OmfSegmentHeader extends OmfRecord { */ private void establishNextBuffer() throws IOException { while (dataUpNext < dataBlocks.size()) { - OmfData data = dataBlocks.get(dataUpNext); - if (pointer < data.getDataOffset()) { + OmfData omfData = dataBlocks.get(dataUpNext); + if (pointer < omfData.getDataOffset()) { // We have some fill to produce before the next section - long size = data.getDataOffset() - pointer; + long size = omfData.getDataOffset() - pointer; if (size > OmfLoader.MAX_UNINITIALIZED_FILL) { throw new IOException( "Unfilled hole in OMF data blocks for segment: " + segmentName); @@ -408,8 +411,8 @@ public class OmfSegmentHeader extends OmfRecord { bufferpointer = 0; return; } - else if (pointer == data.getDataOffset()) { - buffer = data.getByteArray(reader); + else if (pointer == omfData.getDataOffset()) { + buffer = omfData.getByteArray(reader); bufferpointer = 0; dataUpNext++; if (buffer.length == 0) { @@ -421,7 +424,7 @@ public class OmfSegmentHeader extends OmfRecord { dataUpNext++; throw new IOException(String.format( "Segment %s:%s has bad data offset (0x%x) in data block %d...skipping.", - segmentName, className, data.getDataOffset(), dataUpNext - 1)); + segmentName, className, omfData.getDataOffset(), dataUpNext - 1)); } } // We may have filler after the last block diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfSymbolRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfSymbolRecord.java index f7cfbe635e..671e605459 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfSymbolRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf/OmfSymbolRecord.java @@ -35,26 +35,28 @@ public class OmfSymbolRecord extends OmfRecord { private record Reference(OmfString name, Omf2or4 offset, OmfIndex type) {} public OmfSymbolRecord(BinaryReader reader, boolean isStatic) throws IOException { + super(reader); this.isStatic = isStatic; - readRecordHeader(reader); - long max = reader.getPointerIndex() + getRecordLength() - 1; + } + + @Override + public void parseData() throws IOException { boolean hasBigFields = hasBigFields(); - baseGroupIndex = OmfUtils.readIndex(reader); - baseSegmentIndex = OmfUtils.readIndex(reader); + baseGroupIndex = OmfUtils.readIndex(dataReader); + baseSegmentIndex = OmfUtils.readIndex(dataReader); if (baseSegmentIndex.value() == 0) { - baseFrame = reader.readNextUnsignedShort(); + baseFrame = dataReader.readNextUnsignedShort(); } ArrayList symbollist = new ArrayList(); - while (reader.getPointerIndex() < max) { - OmfString name = OmfUtils.readString(reader); - Omf2or4 offset = OmfUtils.readInt2Or4(reader, hasBigFields); - OmfIndex type = OmfUtils.readIndex(reader); + while (dataReader.getPointerIndex() < dataEnd) { + OmfString name = OmfUtils.readString(dataReader); + Omf2or4 offset = OmfUtils.readInt2Or4(dataReader, hasBigFields); + OmfIndex type = OmfUtils.readIndex(dataReader); OmfSymbol subrec = new OmfSymbol(name.str(), type.value(), offset.value(), 0, 0); symbollist.add(subrec); refs.add(new Reference(name, offset, type)); } - readCheckSumByte(reader); symbol = new OmfSymbol[symbollist.size()]; symbollist.toArray(symbol); } @@ -107,5 +109,4 @@ public class OmfSymbolRecord extends OmfRecord { struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); return struct; } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf166/Omf166DepList.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf166/Omf166DepList.java new file mode 100644 index 0000000000..7c42c60fad --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf166/Omf166DepList.java @@ -0,0 +1,81 @@ +/* ### + * 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.bin.format.omf.omf166; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.omf.*; +import ghidra.program.model.data.*; +import ghidra.util.exception.DuplicateNameException; + +public class Omf166DepList extends OmfRecord { + + private record Info(byte type, Byte mark, Integer time, OmfString name) {} + + private List infoList = new ArrayList<>(); + + public Omf166DepList(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + while (dataReader.getPointerIndex() < dataEnd) { + byte iTyp = dataReader.readNextByte(); + switch (iTyp) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + byte mark = dataReader.readNextByte(); + int time = dataReader.readNextInt(); + OmfString name = OmfUtils.readString(dataReader); + infoList.add(new Info(iTyp, mark, time, name)); + break; + case (byte) 0xff: + OmfString invocation = OmfUtils.readString(dataReader); + infoList.add(new Info(iTyp, null, null, invocation)); + break; + default: + throw new OmfException("Unexpected DEPLST iTyp: 0x%x".formatted(iTyp)); + } + } + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType(Omf166RecordTypes.getName(recordType), 0); + struct.add(BYTE, "type", null); + struct.add(WORD, "length", null); + for (Info info : infoList) { + struct.add(BYTE, "iTyp", null); + if (info.mark != null) { + struct.add(BYTE, "mark8", null); + } + if (info.time != null) { + struct.add(DWORD, "time32", null); + } + struct.add(info.name.toDataType(), "name", null); + } + struct.add(BYTE, "checksum", null); + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf166/Omf166RecordTypes.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf166/Omf166RecordTypes.java new file mode 100644 index 0000000000..f0b373b50d --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf166/Omf166RecordTypes.java @@ -0,0 +1,73 @@ +/* ### + * 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.bin.format.omf.omf166; + +import ghidra.app.util.bin.format.omf.OmfUtils; + +/** + * OMF-166 record types + * + * @see OMF-166 Description + */ +public class Omf166RecordTypes { + + public final static int RTXDEF = 0x30; + public final static int DEPLST = 0x70; + public final static int REGMSK = 0x72; + public final static int TYPNEW = 0xF0; + public final static int BLKEND = 0x7C; + public final static int THEADR = 0x80; + public final static int LHEADR = 0x82; + public final static int COMMENT = 0x88; + public final static int MODEND = 0x8A; + public final static int LINNUM = 0x94; + public final static int LNAMES = 0x96; + public final static int LIBLOC = 0xA8; + public final static int LIBNAMES = 0xA6; + public final static int LIBDICT = 0xAA; + public final static int LIBHDR = 0xBA; + public final static int PHEADR = 0xE0; + public final static int PECDEF = 0xE4; + public final static int SSKDEF = 0xE5; + public final static int MODINF = 0xE7; + public final static int TSKDEF = 0xE1; + public final static int REGDEF = 0xE3; + public final static int SEDEF = 0xB0; + public final static int TYPDEF = 0xB2; + public final static int GRPDEF = 0xB1; + public final static int PUBDEF = 0xB3; + public final static int GLBDEF = 0xE6; + public final static int EXTDEF = 0x8C; + public final static int LOCSYM = 0xB5; + public final static int BLKDEF = 0xB7; + public final static int DEBSYM = 0xB6; + public final static int LEDATA = 0xB8; + public final static int PEDATA = 0xB9; + public final static int VECTAB = 0xE9; + public final static int FIXUPP = 0xB4; + public final static int TSKEND = 0xE2; + public final static int XSECDEF = 0xC5; + + /** + * Gets the name of the given record type + * + * @param type The record type + * @return The name of the given record type + */ + public final static String getName(int type) { + return OmfUtils.getRecordName(type, Omf166RecordTypes.class); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51ModuleEnd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51ModuleEnd.java new file mode 100644 index 0000000000..34c0f73fad --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51ModuleEnd.java @@ -0,0 +1,70 @@ +/* ### + * 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.bin.format.omf.omf51; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.omf.*; +import ghidra.program.model.data.*; +import ghidra.util.exception.DuplicateNameException; + +public class Omf51ModuleEnd extends OmfRecord { + + private OmfString moduleName; + private byte regMsk; + + /** + * Creates a new {@link Omf51ModuleEnd} record + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @throws IOException if an IO-related error occurred + */ + public Omf51ModuleEnd(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + moduleName = OmfUtils.readString(dataReader); + dataReader.readNextByte(); + dataReader.readNextByte(); + regMsk = dataReader.readNextByte(); + dataReader.readNextByte(); + } + + /** + * {@return the register mask} + */ + public byte getRegisterMask() { + return regMsk; + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType(Omf51RecordTypes.getName(recordType), 0); + struct.add(BYTE, "type", null); + struct.add(WORD, "length", null); + struct.add(moduleName.toDataType(), "name", null); + struct.add(WORD, "padding", null); + struct.add(BYTE, "REG MSK", null); + struct.add(BYTE, "padding", null); + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51ModuleHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51ModuleHeader.java new file mode 100644 index 0000000000..47afe0072c --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51ModuleHeader.java @@ -0,0 +1,67 @@ +/* ### + * 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.bin.format.omf.omf51; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.omf.*; +import ghidra.program.model.data.*; +import ghidra.util.exception.DuplicateNameException; + +public class Omf51ModuleHeader extends OmfRecord { + + private OmfString moduleName; + private byte trnId; + + /** + * Creates a new {@link Omf51ModuleHeader} record + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @throws IOException if an IO-related error occurred + */ + public Omf51ModuleHeader(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + moduleName = OmfUtils.readString(dataReader); + dataReader.readNextByte(); + trnId = dataReader.readNextByte(); + } + + /** + * {@return the TRN ID} + */ + public byte getTrnId() { + return trnId; + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType(Omf51RecordTypes.getName(recordType), 0); + struct.add(BYTE, "type", null); + struct.add(WORD, "length", null); + struct.add(moduleName.toDataType(), "name", null); + struct.add(BYTE, "padding", null); + struct.add(BYTE, "TRN ID", null); + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordFactory.java new file mode 100644 index 0000000000..8d3e7f5225 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordFactory.java @@ -0,0 +1,83 @@ +/* ### + * 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.bin.format.omf.omf51; + +import static ghidra.app.util.bin.format.omf.omf51.Omf51RecordTypes.*; + +import java.io.IOException; +import java.util.List; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.format.omf.*; +import ghidra.app.util.bin.format.omf.omf166.Omf166RecordTypes; +import ghidra.app.util.bin.format.omf.omf166.Omf166DepList; + +/** + * A class for reading/creating OMF-51 records + */ +public class Omf51RecordFactory extends AbstractOmfRecordFactory { + + /** + * Creates a new {@link Omf51RecordFactory} + * + * @param provider The {@link ByteProvider} that contains the records + */ + public Omf51RecordFactory(ByteProvider provider) { + super(new BinaryReader(provider, true)); + } + + @Override + public OmfRecord readNextRecord() throws IOException, OmfException { + int type = Byte.toUnsignedInt(reader.peekNextByte()); + OmfRecord record = switch (type) { + case ModuleHDR: + yield new Omf51ModuleHeader(reader); + case ModuleEND: + yield new Omf51ModuleEnd(reader); + case Omf166RecordTypes.DEPLST: + yield new Omf166DepList(reader); + case Content: + case Fixup: + case SegmentDEF: + case ScopeDEF: + case DebugItem: + case PublicDEF: + case ExternalDEF: + case LibModLocs: + case LibModName: + case LibDictionary: + case LibHeader: + yield new OmfUnsupportedRecord(reader, Omf51RecordTypes.class); + default: + yield new OmfUnknownRecord(reader); + }; + + record.parseData(); + return record; + } + + @Override + public List getStartRecordTypes() { + return List.of(ModuleHDR, Omf166RecordTypes.DEPLST); + } + + @Override + public int getEndRecordType() { + return ModuleEND; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordTypes.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordTypes.java new file mode 100644 index 0000000000..2248d8932d --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51RecordTypes.java @@ -0,0 +1,50 @@ +/* ### + * 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.bin.format.omf.omf51; + +import ghidra.app.util.bin.format.omf.OmfUtils; + +/** + * OMF-51 record types + * + * @see OMF-51 Object Module Format + */ +public class Omf51RecordTypes { + + public final static int ModuleHDR = 0x02; + public final static int ModuleEND = 0x04; + public final static int Content = 0x06; + public final static int Fixup = 0x08; + public final static int SegmentDEF = 0x0e; + public final static int ScopeDEF = 0x10; + public final static int DebugItem = 0x12; + public final static int PublicDEF = 0x16; + public final static int ExternalDEF = 0x18; + public final static int LibModLocs = 0x26; + public final static int LibModName = 0x28; + public final static int LibDictionary = 0x2a; + public final static int LibHeader = 0x2c; + + /** + * Gets the name of the given record type + * + * @param type The record type + * @return The name of the given record type + */ + public final static String getName(int type) { + return OmfUtils.getRecordName(type, Omf51RecordTypes.class); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/Omf51Loader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/Omf51Loader.java new file mode 100644 index 0000000000..a3af77b00f --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/Omf51Loader.java @@ -0,0 +1,111 @@ +/* ### + * 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.opinion; + +import java.io.IOException; +import java.util.*; + +import ghidra.app.util.MemoryBlockUtils; +import ghidra.app.util.Option; +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.format.omf.*; +import ghidra.app.util.bin.format.omf.omf51.Omf51RecordFactory; +import ghidra.app.util.importer.MessageLog; +import ghidra.program.database.mem.FileBytes; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.data.DataUtilities; +import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * A {@link Loader} for OMF-51 files + */ +public class Omf51Loader extends AbstractProgramWrapperLoader { + public final static String OMF51_NAME = "Object Module Format (OMF-51)"; + public final static long MIN_BYTE_LENGTH = 11; + + @Override + public Collection findSupportedLoadSpecs(ByteProvider provider) throws IOException { + List loadSpecs = new ArrayList<>(); + + if (provider.length() < MIN_BYTE_LENGTH) { + return loadSpecs; + } + + AbstractOmfRecordFactory factory = new Omf51RecordFactory(provider); + try { + OmfRecord first = factory.readNextRecord(); + if (factory.getStartRecordTypes().contains(first.getRecordType()) && + first.validCheckSum()) { + List results = QueryOpinionService.query(getName(), "8051", null); + for (QueryResult result : results) { + loadSpecs.add(new LoadSpec(this, 0, result)); + } + if (loadSpecs.isEmpty()) { + loadSpecs.add(new LoadSpec(this, 0, true)); + } + } + } + catch (IOException | OmfException e) { + // that's ok, not an OMF-51 + } + return loadSpecs; + } + + @Override + protected void load(ByteProvider provider, LoadSpec loadSpec, List