GP-4722: Marking up OMF records

This commit is contained in:
Ryan Kurtz 2024-06-26 10:59:03 -04:00
parent 4b30e484b0
commit 36a707471e
18 changed files with 451 additions and 126 deletions

View File

@ -0,0 +1,46 @@
/* ###
* 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 ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.DuplicateNameException;
public class Omf2or4 implements StructConverter {
private int length;
private long value;
public Omf2or4(int length, long value) {
this.length = length;
this.value = value;
}
public int length() {
return length;
}
public long value() {
return value;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
return length == 2 ? WORD : DWORD;
}
}

View File

@ -32,9 +32,9 @@ public class OmfComdatExternalSymbol extends OmfExternalSymbol {
long max = reader.getPointerIndex() + getRecordLength() - 1;
while (reader.getPointerIndex() < max) {
int nameIndex = OmfRecord.readIndex(reader);
int type = OmfRecord.readIndex(reader);
externalLookups.add(new ExternalLookup(nameIndex, type));
OmfIndex nameIndex = OmfRecord.readIndex(reader);
OmfIndex type = OmfRecord.readIndex(reader);
externalLookups.add(new ExternalLookup(nameIndex.value(), type.value()));
}
readCheckSumByte(reader);

View File

@ -27,8 +27,8 @@ public class OmfComdefRecord extends OmfExternalSymbol {
long max = reader.getPointerIndex() + getRecordLength() - 1;
while (reader.getPointerIndex() < max) {
String name = OmfRecord.readString(reader);
int typeIndex = OmfRecord.readIndex(reader);
OmfString name = OmfRecord.readString(reader);
OmfIndex typeIndex = OmfRecord.readIndex(reader);
byte dataType = reader.readNextByte();
int byteLength = 0;
if (dataType == 0x61) { // FAR data, reads numElements and elSize
@ -40,7 +40,7 @@ public class OmfComdefRecord extends OmfExternalSymbol {
// Values 1 thru 5f plus 61, read the byte length
byteLength = readCommunalLength(reader);
}
symbols.add(new OmfSymbol(name, typeIndex, 0, dataType, byteLength));
symbols.add(new OmfSymbol(name.str(), typeIndex.value(), 0, dataType, byteLength));
}
readCheckSumByte(reader);
}

View File

@ -19,6 +19,8 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class OmfCommentRecord extends OmfRecord {
// Language translator comment
@ -34,7 +36,7 @@ public class OmfCommentRecord extends OmfRecord {
private byte commentType;
private byte commentClass;
private String value;
private OmfString value;
public OmfCommentRecord(BinaryReader reader) throws IOException {
readRecordHeader(reader);
@ -46,7 +48,7 @@ public class OmfCommentRecord extends OmfRecord {
case COMMENT_CLASS_DEFAULT_LIBRARY:
byte[] bytes = reader.readNextByteArray(getRecordLength() -
3 /* 3 = sizeof(commentType+commentClass+trailing_crcbyte*/);
value = new String(bytes, StandardCharsets.US_ASCII); // assuming ASCII
value = new OmfString(bytes.length, new String(bytes, StandardCharsets.US_ASCII)); // assuming ASCII
break;
case COMMENT_CLASS_LIBMOD:
value = readString(reader);
@ -67,6 +69,24 @@ public class OmfCommentRecord extends OmfRecord {
}
public String getValue() {
return value;
return value.str();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
int strlen = getRecordLength() - 3;
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
struct.add(BYTE, "type", null);
struct.add(WORD, "length", "");
struct.add(BYTE, "comment_type", null);
struct.add(BYTE, "comment_class", null);
if (strlen > 0) {
struct.add(new StringDataType(), strlen, "str", null);
}
struct.add(BYTE, "checksum", null);
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
return struct;
}
}

View File

@ -23,21 +23,21 @@ import ghidra.app.util.bin.BinaryReader;
* Object representing data loaded directly into the final image.
*/
public abstract class OmfData extends OmfRecord implements Comparable<OmfData> {
protected int segmentIndex;
protected long dataOffset;
protected OmfIndex segmentIndex;
protected Omf2or4 dataOffset;
/**
* @return get the segments index for this datablock
*/
public int getSegmentIndex() {
return segmentIndex;
return segmentIndex.value();
}
/**
* @return the starting offset, within the loaded image, of this data
*/
public long getDataOffset() {
return dataOffset;
return dataOffset.value();
}
/**
@ -47,7 +47,7 @@ public abstract class OmfData extends OmfRecord implements Comparable<OmfData> {
*/
@Override
public int compareTo(OmfData o) {
return Long.compare(dataOffset, o.dataOffset);
return Long.compare(dataOffset.value(), o.dataOffset.value());
}
/**

View File

@ -27,7 +27,7 @@ public class OmfEnumeratedData extends OmfData {
readRecordHeader(reader);
long start = reader.getPointerIndex();
segmentIndex = OmfRecord.readIndex(reader);
dataOffset = OmfRecord.readInt2Or4(reader, hasBigFields()) & 0xffffffffL;
dataOffset = OmfRecord.readInt2Or4(reader, hasBigFields());
streamOffset = reader.getPointerIndex();
streamLength = getRecordLength() - 1 - (int) (streamOffset - start);
reader.setPointerIndex(streamOffset + streamLength); // Skip over the data when reading header

View File

@ -20,12 +20,18 @@ import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class OmfExternalSymbol extends OmfRecord {
private boolean isStatic;
protected List<OmfSymbol> symbols = new ArrayList<>();
private record Reference(OmfString name, OmfIndex type) {}
private List<Reference> refs = new ArrayList<>();
protected OmfExternalSymbol(boolean isStatic) {
this.isStatic = isStatic;
}
@ -36,9 +42,10 @@ public class OmfExternalSymbol extends OmfRecord {
long max = reader.getPointerIndex() + getRecordLength() - 1;
while (reader.getPointerIndex() < max) {
String name = OmfRecord.readString(reader);
int type = OmfRecord.readIndex(reader);
symbols.add(new OmfSymbol(name, type, 0, 0, 0));
OmfString name = OmfRecord.readString(reader);
OmfIndex type = OmfRecord.readIndex(reader);
refs.add(new Reference(name, type));
symbols.add(new OmfSymbol(name.str(), type.value(), 0, 0, 0));
}
readCheckSumByte(reader);
@ -51,4 +58,19 @@ public class OmfExternalSymbol extends OmfRecord {
public boolean isStatic() {
return isStatic;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
struct.add(BYTE, "type", null);
struct.add(WORD, "length", null);
for (Reference ref : refs) {
struct.add(ref.name.toDataType(), "name", null);
struct.add(ref.type.toDataType(), "type", null);
}
struct.add(BYTE, "checksum", null);
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
return struct;
}
}

View File

@ -17,41 +17,52 @@ package ghidra.app.util.bin.format.omf;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
public class OmfFileHeader extends OmfRecord {
private String objectName; // Name of the object module
private String libModuleName = null; // Name of the module (within a library)
private String translator = null; // Usually the compiler/linker used to produce this object
private OmfString objectName; // Name of the object module
private String libModuleName = null; // Name of the module (within a library)
private String translator = null; // Usually the compiler/linker used to produce this object
private boolean isLittleEndian;
private ArrayList<String> nameList = new ArrayList<String>(); // Indexable List of segment, group, ... names
private ArrayList<OmfSegmentHeader> segments = new ArrayList<OmfSegmentHeader>();
private ArrayList<OmfGroupRecord> groups = new ArrayList<OmfGroupRecord>();
private ArrayList<OmfExternalSymbol> externsymbols = new ArrayList<OmfExternalSymbol>();
private ArrayList<OmfSymbolRecord> symbols = new ArrayList<OmfSymbolRecord>();
private ArrayList<OmfFixupRecord> fixup = new ArrayList<OmfFixupRecord>();
private ArrayList<OmfSegmentHeader> extraSeg = null; // Holds implied segments that don't have official header record
private List<OmfRecord> records = new ArrayList<>();
private List<String> nameList = new ArrayList<>(); // Indexable List of segment, group, ... names
private List<OmfSegmentHeader> segments = new ArrayList<>();
private List<OmfGroupRecord> groups = new ArrayList<>();
private List<OmfExternalSymbol> externsymbols = new ArrayList<>();
private List<OmfSymbolRecord> symbols = new ArrayList<>();
private List<OmfFixupRecord> fixup = new ArrayList<>();
private List<OmfSegmentHeader> extraSeg = null; // Holds implied segments that don't have official header record
// private OmfModuleEnd endModule = null;
private boolean format16bit;
public OmfFileHeader(BinaryReader reader) throws IOException {
readRecordHeader(reader);
objectName = readString(reader); // This is usually the source code filename
objectName = readString(reader); // This is usually the source code filename
readCheckSumByte(reader);
isLittleEndian = reader.isLittleEndian();
}
/**
* {@return the list of records}
*/
public List<OmfRecord> getRecords() {
return records;
}
/**
* This is usually the original source filename
* @return the name
*/
public String getName() {
return objectName;
return objectName.str();
}
/**
@ -88,42 +99,42 @@ public class OmfFileHeader extends OmfRecord {
/**
* @return the list of segments in this file
*/
public ArrayList<OmfSegmentHeader> getSegments() {
public List<OmfSegmentHeader> getSegments() {
return segments;
}
/**
* @return the list of segments which are Borland extensions
*/
public ArrayList<OmfSegmentHeader> getExtraSegments() {
public List<OmfSegmentHeader> getExtraSegments() {
return extraSeg;
}
/**
* @return the list of group records for this file
*/
public ArrayList<OmfGroupRecord> getGroups() {
public List<OmfGroupRecord> getGroups() {
return groups;
}
/**
* @return the list of symbols that are external to this file
*/
public ArrayList<OmfExternalSymbol> getExternalSymbols() {
public List<OmfExternalSymbol> getExternalSymbols() {
return externsymbols;
}
/**
* @return the list of symbols exported by this file
*/
public ArrayList<OmfSymbolRecord> getPublicSymbols() {
public List<OmfSymbolRecord> getPublicSymbols() {
return symbols;
}
/**
* @return the list of relocation records for this file
*/
public ArrayList<OmfFixupRecord> getFixups() {
public List<OmfFixupRecord> getFixups() {
return fixup;
}
@ -313,14 +324,15 @@ public class OmfFileHeader extends OmfRecord {
public static OmfFileHeader parse(BinaryReader reader, TaskMonitor monitor, MessageLog log)
throws IOException, OmfException {
OmfRecord record = OmfRecord.readRecord(reader);
if (!(record instanceof OmfFileHeader)) {
if (!(record instanceof OmfFileHeader header)) {
throw new OmfException("Object file does not start with proper header");
}
OmfFileHeader header = (OmfFileHeader) record;
header.records.add(header);
OmfData lastDataBlock = null;
while (true) {
record = OmfRecord.readRecord(reader);
header.records.add(record);
if (monitor.isCancelled()) {
break;
@ -403,8 +415,8 @@ public class OmfFileHeader extends OmfRecord {
* @param groups is the list of specific segments that are grouped together in memory
* @throws OmfException for malformed index/alignment/combining fields
*/
public static void doLinking(long startAddress, ArrayList<OmfSegmentHeader> segments,
ArrayList<OmfGroupRecord> groups) throws OmfException {
public static void doLinking(long startAddress, List<OmfSegmentHeader> segments,
List<OmfGroupRecord> groups) throws OmfException {
// Link anything in groups first
for (int i = 0; i < groups.size(); ++i) {
OmfGroupRecord group = groups.get(i);
@ -489,4 +501,16 @@ public class OmfFileHeader extends OmfRecord {
private static void logRecord(String description, OmfRecord record, MessageLog log) {
log.appendMsg(description + " (" + record + ")");
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
struct.add(BYTE, "type", null);
struct.add(WORD, "length", null);
struct.add(objectName.toDataType(), "name", null);
struct.add(BYTE, "checksum", null);
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
return struct;
}
}

View File

@ -67,10 +67,10 @@ public class OmfFixupRecord extends OmfRecord {
private byte first;
private byte hiFixup;
private byte fixData;
private int index;
private int frameDatum;
private int targetDatum;
private int targetDisplacement;
private OmfIndex index;
private OmfIndex frameDatum;
private OmfIndex targetDatum;
private Omf2or4 targetDisplacement;
/**
* Read the next subrecord from the input reader
@ -85,7 +85,7 @@ public class OmfFixupRecord extends OmfRecord {
int method;
final var rec = new Subrecord();
rec.first = reader.readNextByte();
rec.index = -1;
rec.index = new OmfIndex(1, -1);
if (rec.isThreadSubrecord()) {
method = rec.getThreadMethod();
if (method < 4) {
@ -93,8 +93,8 @@ public class OmfFixupRecord extends OmfRecord {
}
return rec;
}
rec.targetDisplacement = 0;
rec.targetDatum = 0;
rec.targetDisplacement = new Omf2or4(2, 0);
rec.targetDatum = new OmfIndex(1, 0);
rec.hiFixup = reader.readNextByte();
rec.fixData = reader.readNextByte();
method = rec.getFrameMethod();
@ -135,7 +135,7 @@ public class OmfFixupRecord extends OmfRecord {
* @return Get the index for explicit thread or frame
*/
public int getIndex() {
return index;
return index.value();
}
/**
@ -170,11 +170,11 @@ public class OmfFixupRecord extends OmfRecord {
}
public int getTargetDatum() {
return targetDatum;
return targetDatum.value();
}
public int getTargetDisplacement() {
return targetDisplacement;
return (int) targetDisplacement.value();
}
public int getLocationType() {

View File

@ -17,6 +17,7 @@ package ghidra.app.util.bin.format.omf;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.address.Address;
@ -24,7 +25,7 @@ import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Language;
public class OmfGroupRecord extends OmfRecord {
private int groupNameIndex;
private OmfIndex groupNameIndex;
private String groupName;
private long vma = -1; // Assigned (by linker) starting address of the whole group
private GroupSubrecord[] group;
@ -72,7 +73,7 @@ public class OmfGroupRecord extends OmfRecord {
}
public int getSegmentIndex(int i) {
return group[i].segmentIndex;
return group[i].segmentIndex.value();
}
public Address getAddress(Language language) {
@ -80,19 +81,19 @@ public class OmfGroupRecord extends OmfRecord {
return addrSpace.getAddress(vma);
}
public void resolveNames(ArrayList<String> nameList) throws OmfException {
if (groupNameIndex <= 0) {
public void resolveNames(List<String> nameList) throws OmfException {
if (groupNameIndex.value() <= 0) {
throw new OmfException("Cannot have unused group name");
}
if (groupNameIndex > nameList.size()) {
if (groupNameIndex.value() > nameList.size()) {
throw new OmfException("Group name index out of bounds");
}
groupName = nameList.get(groupNameIndex - 1);
groupName = nameList.get(groupNameIndex.value() - 1);
}
public static class GroupSubrecord {
private byte componentType;
private int segmentIndex;
private OmfIndex segmentIndex;
public static GroupSubrecord read(BinaryReader reader) throws IOException {
GroupSubrecord subrec = new GroupSubrecord();

View File

@ -0,0 +1,46 @@
/* ###
* 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 ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.DuplicateNameException;
public class OmfIndex implements StructConverter {
private int length;
private int value;
public OmfIndex(int length, int value) {
this.length = length;
this.value = value;
}
public int length() {
return length;
}
public int value() {
return value;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
return length == 2 ? WORD : BYTE;
}
}

View File

@ -78,7 +78,7 @@ public class OmfIteratedData extends OmfData {
* Contain the definition of one part of a datablock with possible recursion
*/
public static class DataBlock {
private int repeatCount;
private Omf2or4 repeatCount;
private int blockCount;
private byte[] simpleBlock = null;
private DataBlock[] nestedBlock = null;
@ -86,7 +86,7 @@ public class OmfIteratedData extends OmfData {
public static DataBlock read(BinaryReader reader, boolean hasBigFields) throws IOException {
DataBlock subblock = new DataBlock();
subblock.repeatCount = OmfRecord.readInt2Or4(reader, hasBigFields);
subblock.blockCount = reader.readNextShort() & 0xffff;
subblock.blockCount = reader.readNextUnsignedShort();
if (subblock.blockCount == 0) {
int size = reader.readNextByte() & 0xff;
subblock.simpleBlock = new byte[size];
@ -110,7 +110,7 @@ public class OmfIteratedData extends OmfData {
* @return The position after the block
*/
public int fillBuffer(byte[] buffer, int pos) {
for (int i = 0; i < repeatCount; ++i) {
for (int i = 0; i < (int) repeatCount.value(); ++i) {
if (simpleBlock != null) {
for (byte element : simpleBlock) {
buffer[pos] = element;
@ -139,7 +139,7 @@ public class OmfIteratedData extends OmfData {
length += block.getLength();
}
}
return length * repeatCount;
return length * (int) repeatCount.value();
}
/**

View File

@ -17,24 +17,39 @@ package ghidra.app.util.bin.format.omf;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class OmfNamesRecord extends OmfRecord {
private ArrayList<String> name;
private List<OmfString> names = new ArrayList<>();
public OmfNamesRecord(BinaryReader reader) throws IOException {
readRecordHeader(reader);
long max = reader.getPointerIndex() + getRecordLength() - 1;
name = new ArrayList<String>();
while (reader.getPointerIndex() < max) {
String nm = OmfRecord.readString(reader);
name.add(nm);
names.add(OmfRecord.readString(reader));
}
readCheckSumByte(reader);
}
public void appendNames(ArrayList<String> namelist) {
namelist.addAll(name);
public void appendNames(List<String> namelist) {
namelist.addAll(names.stream().map(name -> name.str()).toList());
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
struct.add(BYTE, "type", null);
struct.add(WORD, "length", null);
for (OmfString name : names) {
struct.add(name.toDataType(), -1, "name", null);
}
struct.add(BYTE, "checksum", null);
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
return struct;
}
}

View File

@ -20,8 +20,11 @@ import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public abstract class OmfRecord {
public abstract class OmfRecord implements StructConverter {
public final static byte RHEADR = (byte) 0x6E; // Obsolete
public final static byte REGINT = (byte) 0x70; // Obsolete
public final static byte REDATA = (byte) 0x72; // Obsolete
@ -68,6 +71,8 @@ public abstract class OmfRecord {
public final static byte START = (byte) 0xF0;
public final static byte END = (byte) 0xF1;
protected static final String CATEGORY_PATH = "/OMF";
protected byte recordType;
protected int recordLength;
protected long recordOffset;
@ -120,22 +125,26 @@ public abstract class OmfRecord {
return (reader.readNextByte() & 0xff);
}
public static int readInt2Or4(BinaryReader reader, boolean isBig) throws IOException {
if (isBig)
return reader.readNextInt();
return (reader.readNextShort() & 0xffff);
public static Omf2or4 readInt2Or4(BinaryReader reader, boolean isBig) throws IOException {
if (isBig) {
return new Omf2or4(4, reader.readNextInt());
}
return new Omf2or4(2, reader.readNextUnsignedShort());
}
public static int readIndex(BinaryReader reader) throws IOException {
public static OmfIndex readIndex(BinaryReader reader) throws IOException {
int length;
int indexWord;
byte firstByte = reader.readNextByte();
if ((firstByte & 0x80) != 0) {
indexWord = (firstByte & 0x7f) * 0x100 + (reader.readNextByte() & 0xff);
length = 2;
}
else {
indexWord = firstByte;
length = 1;
}
return indexWord;
return new OmfIndex(length, indexWord);
}
public static OmfRecord readRecord(BinaryReader reader) throws IOException, OmfException {
@ -215,9 +224,9 @@ public abstract class OmfRecord {
* @return the read OMF string
* @throws IOException if an IO-related error occurred
*/
public static String readString(BinaryReader reader) throws IOException {
public static OmfString readString(BinaryReader reader) throws IOException {
int count = reader.readNextByte() & 0xff;
return reader.readNextAsciiString(count);
return new OmfString(count, reader.readNextAsciiString(count));
}
/**
@ -249,4 +258,16 @@ public abstract class OmfRecord {
return String.format("name: %s, type: 0x%x, offset: 0x%x, length: 0x%x",
getRecordName(recordType & (byte) 0xfe), recordType, recordOffset, recordLength);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
struct.add(BYTE, "type", null);
struct.add(WORD, "length", null);
struct.add(new ArrayDataType(BYTE, getRecordLength() - 1, 1), "contents", null);
struct.add(BYTE, "checksum", null);
struct.setCategoryPath(new CategoryPath(CATEGORY_PATH));
return struct;
}
}

View File

@ -17,24 +17,25 @@ package ghidra.app.util.bin.format.omf;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.OmfLoader;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Language;
import ghidra.util.exception.DuplicateNameException;
public class OmfSegmentHeader extends OmfRecord {
private byte segAttr; // first byte of Segment Attributes
private int frameNumber;
private int offset;
private long segmentLength;
private int segmentNameIndex;
private int classNameIndex;
private int overlayNameIndex;
private Omf2or4 segmentLength;
private OmfIndex segmentNameIndex;
private OmfIndex classNameIndex;
private OmfIndex overlayNameIndex;
private String segmentName;
private String className;
private String overlayName;
@ -48,10 +49,10 @@ public class OmfSegmentHeader extends OmfRecord {
OmfSegmentHeader(int num, int datatype) {
// generate a special Borland header
segAttr = (byte) 0xa9;
segmentLength = 0;
segmentNameIndex = 0;
classNameIndex = 0;
overlayNameIndex = 0;
segmentLength = new Omf2or4(2,0);
segmentNameIndex = new OmfIndex(1, 0);
classNameIndex = new OmfIndex(1, 0);
overlayNameIndex = new OmfIndex(1, 0);
overlayName = "";
if (datatype == 1) {
segmentName = "EXTRATEXT_";
@ -90,7 +91,7 @@ public class OmfSegmentHeader extends OmfRecord {
offset = reader.readNextByte() & 0xff;
vma = (long) frameNumber + offset;
}
segmentLength = OmfRecord.readInt2Or4(reader, hasBigFields) & 0xffffffffL;
segmentLength = OmfRecord.readInt2Or4(reader, hasBigFields);
segmentNameIndex = OmfRecord.readIndex(reader);
classNameIndex = OmfRecord.readIndex(reader);
overlayNameIndex = OmfRecord.readIndex(reader);
@ -98,10 +99,10 @@ public class OmfSegmentHeader extends OmfRecord {
int B = (segAttr >> 1) & 1;
if (B == 1) { // Ignore the segmentLength field
if (getRecordType() == OmfRecord.SEGDEF) {
segmentLength = 0x10000L; // Exactly 64K segment
segmentLength = new Omf2or4(segmentLength.length(), 0x10000L); // Exactly 64K segment
}
else {
segmentLength = 0x100000000L; // Exactly 4G segment
segmentLength = new Omf2or4(segmentLength.length(), 0x100000000L); // Exactly 4G segment
}
}
}
@ -189,7 +190,7 @@ public class OmfSegmentHeader extends OmfRecord {
* @return the length of the segment in bytes
*/
public long getSegmentLength() {
return segmentLength;
return segmentLength.value();
}
/**
@ -277,7 +278,7 @@ public class OmfSegmentHeader extends OmfRecord {
throw new OmfException("Unsupported alignment type");
}
vma = firstValidAddress;
firstValidAddress = vma + segmentLength;
firstValidAddress = vma + segmentLength.value();
return firstValidAddress;
}
@ -288,33 +289,33 @@ public class OmfSegmentHeader extends OmfRecord {
* @param nameList is the array of names associated with the file
* @throws OmfException for improper name indices
*/
protected void resolveNames(ArrayList<String> nameList) throws OmfException {
if (segmentNameIndex == 0) {
protected void resolveNames(List<String> nameList) throws OmfException {
if (segmentNameIndex.value() == 0) {
segmentName = ""; // Name is unused
}
else {
if (segmentNameIndex > nameList.size()) {
if (segmentNameIndex.value() > nameList.size()) {
throw new OmfException("Segment name index out of bounds");
}
segmentName = nameList.get(segmentNameIndex - 1);
segmentName = nameList.get(segmentNameIndex.value() - 1);
}
if (classNameIndex == 0) {
if (classNameIndex.value() == 0) {
className = "";
}
else {
if (classNameIndex > nameList.size()) {
if (classNameIndex.value() > nameList.size()) {
throw new OmfException("Class name index out of bounds");
}
className = nameList.get(classNameIndex - 1);
className = nameList.get(classNameIndex.value() - 1);
}
if (overlayNameIndex == 0) {
if (overlayNameIndex.value() == 0) {
overlayName = "";
}
else {
if (overlayNameIndex > nameList.size()) {
if (overlayNameIndex.value() > nameList.size()) {
throw new OmfException("Overlay name index out of bounds");
}
overlayName = nameList.get(overlayNameIndex - 1);
overlayName = nameList.get(overlayNameIndex.value() - 1);
}
// Once we know the class name, we can make some educated guesses about read/write/exec permissions
@ -347,8 +348,8 @@ public class OmfSegmentHeader extends OmfRecord {
*/
protected void appendEnumeratedData(OmfEnumeratedData rec) {
long blockend = rec.getDataOffset() + rec.getLength();
if (blockend > segmentLength) {
segmentLength = blockend;
if (blockend > segmentLength.value()) {
segmentLength = new Omf2or4(segmentLength.length(), blockend);
}
dataBlocks.add(rec);
}
@ -380,7 +381,7 @@ public class OmfSegmentHeader extends OmfRecord {
this.log = log;
pointer = 0;
dataUpNext = 0;
if (pointer < segmentLength) {
if (pointer < segmentLength.value()) {
establishNextBuffer();
}
}
@ -423,7 +424,7 @@ public class OmfSegmentHeader extends OmfRecord {
}
}
// We may have filler after the last block
long size = segmentLength - pointer;
long size = segmentLength.value() - pointer;
if (size > OmfLoader.MAX_UNINITIALIZED_FILL) {
throw new IOException("Large hole at the end of OMF segment: " + segmentName);
}
@ -436,7 +437,7 @@ public class OmfSegmentHeader extends OmfRecord {
@Override
public int read() throws IOException {
if (pointer < segmentLength) {
if (pointer < segmentLength.value()) {
if (bufferpointer < buffer.length) {
pointer++;
return buffer[bufferpointer++] & 0xff;
@ -454,4 +455,25 @@ public class OmfSegmentHeader extends OmfRecord {
}
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
struct.add(BYTE, "type", null);
struct.add(WORD, "length", null);
struct.add(BYTE, "segment_attr", null);
int A = (segAttr >> 5) & 7;
if (A == 0) {
struct.add(WORD, "frame_number", null);
struct.add(BYTE, "offset", null);
}
struct.add(segmentLength.toDataType(), "segment_length", null);
struct.add(segmentNameIndex.toDataType(), "segment_name_index", null);
struct.add(classNameIndex.toDataType(), "class_name_index", null);
struct.add(overlayNameIndex.toDataType(), "overlay_name_index", null);
struct.add(BYTE, "checksum", null);
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
return struct;
}
}

View File

@ -0,0 +1,54 @@
/* ###
* 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 ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class OmfString implements StructConverter {
private int length;
private String str;
public OmfString(int length, String str) {
this.length = length;
this.str = str;
}
public int length() {
return length;
}
public String str() {
return str;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
if (length == 0) {
return BYTE;
}
StructureDataType struct = new StructureDataType("OmfString", 0);
struct.add(BYTE, "length", "");
struct.add(new StringDataType(), length, "str", null);
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
return struct;
}
}

View File

@ -20,13 +20,18 @@ import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class OmfSymbolRecord extends OmfRecord {
private int baseGroupIndex;
private int baseSegmentIndex;
private OmfIndex baseGroupIndex;
private OmfIndex baseSegmentIndex;
private int baseFrame;
private boolean isStatic;
private OmfSymbol[] symbol;
private List<Reference> refs = new ArrayList<>();
private record Reference(OmfString name, Omf2or4 offset, OmfIndex type) {}
public OmfSymbolRecord(BinaryReader reader, boolean isStatic) throws IOException {
this.isStatic = isStatic;
@ -35,17 +40,18 @@ public class OmfSymbolRecord extends OmfRecord {
boolean hasBigFields = hasBigFields();
baseGroupIndex = OmfRecord.readIndex(reader);
baseSegmentIndex = OmfRecord.readIndex(reader);
if (baseSegmentIndex == 0) {
baseFrame = reader.readNextShort() & 0xffff;
if (baseSegmentIndex.value() == 0) {
baseFrame = reader.readNextUnsignedShort();
}
ArrayList<OmfSymbol> symbollist = new ArrayList<OmfSymbol>();
while (reader.getPointerIndex() < max) {
String name = OmfRecord.readString(reader);
long offset = OmfRecord.readInt2Or4(reader, hasBigFields) & 0xffffffffL;
int type = OmfRecord.readIndex(reader);
OmfSymbol subrec = new OmfSymbol(name, type, offset, 0, 0);
OmfString name = OmfRecord.readString(reader);
Omf2or4 offset = OmfRecord.readInt2Or4(reader, hasBigFields);
OmfIndex type = OmfRecord.readIndex(reader);
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()];
@ -57,11 +63,11 @@ public class OmfSymbolRecord extends OmfRecord {
}
public int getGroupIndex() {
return baseGroupIndex;
return baseGroupIndex.value();
}
public int getSegmentIndex() {
return baseSegmentIndex;
return baseSegmentIndex.value();
}
public int numSymbols() {
@ -76,4 +82,29 @@ public class OmfSymbolRecord extends OmfRecord {
return List.of(symbol);
}
public int getBaseFrame() {
return baseFrame;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
struct.add(BYTE, "type", null);
struct.add(WORD, "length", null);
struct.add(baseGroupIndex.toDataType(), "base_group_index", null);
struct.add(baseSegmentIndex.toDataType(), "base_segment_index", null);
if (baseSegmentIndex.value() == 0) {
struct.add(WORD, "base_frame", null);
}
for (Reference ref : refs) {
struct.add(ref.name.toDataType(), "name", null);
struct.add(ref.offset.toDataType(), "offset", null);
struct.add(ref.type.toDataType(), "type", null);
}
struct.add(BYTE, "checksum", null);
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
return struct;
}
}

View File

@ -27,9 +27,9 @@ import ghidra.app.util.bin.format.omf.*;
import ghidra.app.util.bin.format.omf.OmfFixupRecord.Subrecord;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Undefined;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
@ -47,7 +47,7 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
public final static long IMAGE_BASE = 0x2000; // Base offset to start loading segments
public final static long MAX_UNINITIALIZED_FILL = 0x2000; // Maximum zero bytes added to pad initialized segments
private ArrayList<OmfSymbol> externsyms = new ArrayList<>();
private List<OmfSymbol> externsyms = new ArrayList<>();
/**
* OMF usually stores a string describing the compiler that produced it in a
@ -137,19 +137,42 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
// We don't use the file bytes to create block because the bytes are manipulated before
// forming the block. Creating the FileBytes anyway in case later we want access to all
// the original bytes.
MemoryBlockUtils.createFileBytes(program, provider, monitor);
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
try {
processSegmentHeaders(reader, header, program, monitor, log);
processPublicSymbols(header, program, monitor, log);
processExternalSymbols(header, program, monitor, log);
processRelocations(header, program, monitor, log);
markupRecords(program, fileBytes, header, log, monitor);
}
catch (AddressOverflowException e) {
throw new IOException(e);
}
}
private void markupRecords(Program program, FileBytes fileBytes, OmfFileHeader fileHeader,
MessageLog log, TaskMonitor monitor) {
monitor.setMessage("Marking up records...");
int size =
fileHeader.getRecords().stream().mapToInt(r -> r.getRecordLength() + 3).sum();
try {
Address recordSpaceAddr = AddressSpace.OTHER_SPACE.getAddress(0);
MemoryBlock headerBlock = MemoryBlockUtils.createInitializedBlock(program, true,
"RECORDS", recordSpaceAddr, fileBytes, 0, size, "", "", false,
false, false, log);
Address start = headerBlock.getStart();
for (OmfRecord record : fileHeader.getRecords()) {
DataUtilities.createData(program, start.add(record.getRecordOffset()),
record.toDataType(), -1, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
}
catch (Exception e) {
log.appendMsg("Failed to markup records");
}
}
/**
* Log a (hopefully) descriptive error, if we can't process a specific relocation
* @param program is the Program
@ -181,7 +204,7 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
MessageLog log) {
Language language = program.getLanguage();
OmfFixupRecord.Subrecord[] targetThreads = new Subrecord[4];
ArrayList<OmfGroupRecord> groups = header.getGroups();
List<OmfGroupRecord> groups = header.getGroups();
long targetAddr; // Address of item being referred to
Address locAddress; // Location of data to be patched
DataConverter converter = DataConverter.getInstance(!header.isLittleEndian());
@ -364,7 +387,7 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
final Language language = program.getLanguage();
ArrayList<OmfSegmentHeader> segments = header.getSegments();
List<OmfSegmentHeader> segments = header.getSegments();
for (OmfSegmentHeader segment : segments) {
if (monitor.isCancelled()) {
break;
@ -432,9 +455,9 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
MessageLog log) {
SymbolTable symbolTable = program.getSymbolTable();
ArrayList<OmfSymbolRecord> symbols = header.getPublicSymbols();
ArrayList<OmfSegmentHeader> segments = header.getSegments();
ArrayList<OmfGroupRecord> groups = header.getGroups();
List<OmfSymbolRecord> symbols = header.getPublicSymbols();
List<OmfSegmentHeader> segments = header.getSegments();
List<OmfGroupRecord> groups = header.getGroups();
Language language = program.getLanguage();
monitor.setMessage("Creating Public Symbols");
@ -536,7 +559,7 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
private void processExternalSymbols(OmfFileHeader header, Program program, TaskMonitor monitor,
MessageLog log) {
ArrayList<OmfExternalSymbol> symbolrecs = header.getExternalSymbols();
List<OmfExternalSymbol> symbolrecs = header.getExternalSymbols();
if (symbolrecs.size() == 0) {
return;
}