From 9fe70d3828365541f5eb6d51e3b053d16eacbcea Mon Sep 17 00:00:00 2001 From: Gravelbones Date: Wed, 4 Jan 2023 20:16:57 +0100 Subject: [PATCH 1/5] OMf format: Refactor relocation code and add type 2 and 3 (Fixes: #4777, #4793) --- .../app/util/bin/format/omf/OmfData.java | 30 +- .../bin/format/omf/OmfEnumeratedData.java | 33 +- .../util/bin/format/omf/OmfFileHeader.java | 2 +- .../util/bin/format/omf/OmfFixupRecord.java | 391 ++++++------------ .../util/bin/format/omf/OmfIteratedData.java | 45 +- .../app/util/bin/format/omf/OmfModuleEnd.java | 16 +- .../util/bin/format/omf/OmfSymbolRecord.java | 6 + .../ghidra/app/util/opinion/OmfLoader.java | 233 +++++++---- 8 files changed, 361 insertions(+), 395 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfData.java index c0d9b803b3..5432199284 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfData.java @@ -22,17 +22,37 @@ import ghidra.app.util.bin.BinaryReader; /** * Object representing data loaded directly into the final image. */ -public interface OmfData extends Comparable { +public abstract class OmfData extends OmfRecord implements Comparable { + protected int segmentIndex; + protected long dataOffset; + + /** + * @return get the segments index for this datablock + */ + public int getSegmentIndex() { + return segmentIndex; + } /** * @return the starting offset, within the loaded image, of this data */ - public long getDataOffset(); + public long getDataOffset() { + return dataOffset; + } + + /** + * Compare datablocks by data offset + * @return -1 for lower address, 0 for same adress, 1 for higher address + */ + @Override + public int compareTo(OmfData o) { + return Long.compare(dataOffset, o.dataOffset); + } /** * @return the length of this data in bytes */ - public int getLength(); + abstract public int getLength(); /** * Create a byte array holding the data represented by this object. The length @@ -41,10 +61,10 @@ public interface OmfData extends Comparable { * @return allocated and filled byte array * @throws IOException for problems accessing data through the reader */ - public byte[] getByteArray(BinaryReader reader) throws IOException; + abstract public byte[] getByteArray(BinaryReader reader) throws IOException; /** * @return true if this is a block entirely of zeroes */ - public boolean isAllZeroes(); + abstract public boolean isAllZeroes(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfEnumeratedData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfEnumeratedData.java index 0f51568299..13abdcbd63 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfEnumeratedData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfEnumeratedData.java @@ -19,9 +19,7 @@ import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -public class OmfEnumeratedData extends OmfRecord implements OmfData { - private int segmentIndex; - private long dataOffset; +public class OmfEnumeratedData extends OmfData { private long streamOffset; // Position in stream where data starts private int streamLength; // Number of bytes of data @@ -36,29 +34,18 @@ public class OmfEnumeratedData extends OmfRecord implements OmfData { readCheckSumByte(reader); } - public int getSegmentIndex() { - return segmentIndex; - } - - @Override - public long getDataOffset() { - return dataOffset; - } - + /** + * @return The length of the block + */ @Override public int getLength() { return streamLength; } - @Override - public int compareTo(OmfData o) { - long otherOffset = o.getDataOffset(); - if (otherOffset == dataOffset) { - return 0; - } - return (dataOffset < otherOffset) ? -1 : 1; - } - + /** + * @param The inputfile to read from + * @return The array of bytes + */ @Override public byte[] getByteArray(BinaryReader reader) throws IOException { reader.setPointerIndex(streamOffset); @@ -66,6 +53,10 @@ public class OmfEnumeratedData extends OmfRecord implements OmfData { return buffer; } + /** + * Assume that all zeros are done with OmfIteratedData + * @return True if the block is all zeroes + */ @Override public boolean isAllZeroes() { return false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFileHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFileHeader.java index b2154f0ae6..39c79f9d51 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFileHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFileHeader.java @@ -306,7 +306,7 @@ public class OmfFileHeader extends OmfRecord { throw new OmfException("Object file does not start with proper header"); } OmfFileHeader header = (OmfFileHeader) record; - Object lastDataBlock = null; + OmfData lastDataBlock = null; while (true) { record = OmfRecord.readRecord(reader); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFixupRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFixupRecord.java index 6bf157cdb7..7575942dfc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFixupRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFixupRecord.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,310 +19,175 @@ import java.io.IOException; import java.util.ArrayList; import ghidra.app.util.bin.BinaryReader; -import ghidra.program.model.address.Address; -import ghidra.program.model.lang.Language; public class OmfFixupRecord extends OmfRecord { + private final Subrecord[] subrecs; + private OmfData lastData = null; - private Subrecord[] subrecs; - private OmfEnumeratedData lastLEData = null; - private OmfIteratedData lastLIData = null; - + /** + * Read a Fixup record from the input reader + * @param reader The actual reader + * @throws IOException + */ public OmfFixupRecord(BinaryReader reader) throws IOException { ArrayList subreclist = new ArrayList(); - boolean hasBigFields = ((getRecordType() & 1) != 0); readRecordHeader(reader); long max = reader.getPointerIndex() + getRecordLength() - 1; while (reader.getPointerIndex() < max) { - byte peek = reader.peekNextByte(); - if ((peek & 0x80) == 0) { - ThreadSubrecord subrec = ThreadSubrecord.readThreadSubrecord(reader, hasBigFields); - subreclist.add(subrec); - } - else { - FixupSubrecord subrec = FixupSubrecord.readFixupSubrecord(reader, hasBigFields); - subreclist.add(subrec); - } + subreclist.add(Subrecord.readSubrecord(reader, hasBigFields())); } subrecs = new Subrecord[subreclist.size()]; subreclist.toArray(subrecs); readCheckSumByte(reader); } - public void setDataBlock(Object last) { - if (last instanceof OmfEnumeratedData) { - lastLEData = (OmfEnumeratedData) last; - lastLIData = null; - } - else { - lastLIData = (OmfIteratedData) last; - lastLEData = null; - } + /** + * @param last The Datablock this fixup record is meant for + */ + public void setDataBlock(OmfData last) { + lastData = last; } + /** + * @return The datablock this fixup record is meant for + */ + public OmfData getDataBlock() { + return lastData; + } + + /** + * @return The array of subrecords + */ public Subrecord[] getSubrecords() { return subrecs; } - public static class FixupState { - public Language language; - OmfFileHeader header; - public ThreadSubrecord[] frameThreads = new ThreadSubrecord[4]; - public ThreadSubrecord[] targetThreads = new ThreadSubrecord[4]; - public OmfFixupRecord currentFixupRecord; - public ArrayList groups; - public ArrayList externals; - public int frameState; // Frame of item being referred to - public long targetState; // Address of item being referred to - public Address locAddress; // Location of data to be patched - public boolean M; // true for segment-relative, false for self-relative - public int locationType; - - public FixupState(OmfFileHeader header, ArrayList externsyms, Language lang) { - for (int i = 0; i < 4; ++i) { - frameThreads[i] = null; - targetThreads[i] = null; - } - this.header = header; - groups = header.getGroups(); - externals = externsyms; - language = lang; - } - - public void clear() { - targetState = -1; - locAddress = null; - locationType = -1; - } - } - public static class Subrecord { - private boolean isThread; - - public Subrecord(boolean isthread) { - isThread = isthread; - } - - public boolean isThread() { - return isThread; - } - } - - public static class ThreadSubrecord extends Subrecord { - private byte type; - private int index; - - public ThreadSubrecord() { - super(true); - } - - public int getMethod() { - return (type >> 2) & 7; - } - - public int getIndex() { - return index; - } - - public boolean isFrameThread() { - return ((type >> 6) & 1) != 0; - } - - public int getThreadNum() { - return (type & 3); - } - - public void updateState(FixupState state) { - if (isFrameThread()) { - state.frameThreads[getThreadNum()] = this; - } - else { - state.targetThreads[getThreadNum()] = this; - } - } - - public static ThreadSubrecord readThreadSubrecord(BinaryReader reader, boolean hasBigFields) - throws IOException { - ThreadSubrecord thread = new ThreadSubrecord(); - thread.type = reader.readNextByte(); - int method = thread.getMethod(); - if (method >= 4 && thread.isFrameThread()) { - thread.index = -1; - } - else { - thread.index = OmfRecord.readInt1Or2(reader, hasBigFields); - } - return thread; - } - } - - public static class FixupTarget { + private byte first; + private byte hiFixup; private byte fixData; + private int index; private int frameDatum; private int targetDatum; private int targetDisplacement; + /** + * Read the next subrecord from the input reader + * + * @param reader The input file + * @param hasBigFields Is this 16 or 32 bit values + * @return The read subrecord + * @throws IOException + */ + public static Subrecord readSubrecord(BinaryReader reader, boolean hasBigFields) + throws IOException { + int method; + final var rec = new Subrecord(); + rec.first = reader.readNextByte(); + rec.index = -1; + if (rec.isThreadSubrecord()) { + method = rec.getThreadMethod(); + if (method < 4) { + rec.index = readIndex(reader); + } + return rec; + } + rec.targetDisplacement = 0; + rec.targetDatum = 0; + rec.hiFixup = reader.readNextByte(); + rec.fixData = reader.readNextByte(); + method = rec.getFrameMethod(); + if (!rec.isFrameThread() && method < 3) { // F=0 (explicit frame method (and datum)) + rec.frameDatum = readIndex(reader); + } + if (!rec.isTargetThread()) { // T=0 (explicit target) + rec.targetDatum = readIndex(reader); + } + if ((rec.fixData & 0x04) == 0) { // P=0 + rec.targetDisplacement = readInt2Or4(reader, hasBigFields); + } + return rec; + } + + /** + * @return True if this is a Thread subrecord type + */ + public boolean isThreadSubrecord() { + return (first & 0x80) == 0; + } + + /** + * @return The method value from a Thread subrecord + */ + public int getThreadMethod() { + return first >> 2 & 7; + } + + /** + * @return True if this is a frame reference + */ + public boolean isFrameInSubThread() { + return (first & 0x40) != 0; + } + + /** + * @return Get the index for explicit thread or frame + */ + public int getIndex() { + return index; + } + + /** + * @return Get the thread index from flag + */ + public int getThreadNum() { + return first & 3; + } + public boolean isFrameThread() { - return ((fixData >> 7) & 1) != 0; + return (fixData & 0x80) != 0; } public boolean isTargetThread() { - return ((fixData >> 3) & 1) != 0; + return (fixData & 0x08) != 0; } public int getFrameMethod() { - return ((fixData >> 4) & 7); + return fixData >> 4 & 7; } - public int getP() { - int res = (fixData >> 2) & 1; - return res; + public int getFixThreadNum() { + return fixData & 3; } - public void resolveFrame(FixupState state) throws OmfException { - int method; - int index; - if (isFrameThread()) { - // Frame datum from a thread - int threadnum = ((fixData >> 4) & 3); - ThreadSubrecord subrec = state.frameThreads[threadnum]; - method = subrec.getMethod(); - index = subrec.getIndex(); - } - else { - method = getFrameMethod(); - index = frameDatum; - } - switch (method) { - case 0: // Index is for a segment - state.frameState = state.header.resolveSegment(index).getFrameDatum(); - break; - case 1: // Index is for a group - state.frameState = state.groups.get(index - 1).getFrameDatum(); - break; - case 2: // Index is for an external symbol - state.frameState = state.externals.get(index - 1).getFrameDatum(); - break; - case 4: // Segment Index grabbed from datablock - if (state.currentFixupRecord.lastLEData != null) { - index = state.currentFixupRecord.lastLEData.getSegmentIndex(); - } - else { - index = state.currentFixupRecord.lastLIData.getSegmentIndex(); - } - state.frameState = state.header.resolveSegment(index).getFrameDatum(); - break; - case 5: // Frame determined by target - // TODO: Fill this in properly - break; - default: - state.frameState = -1; // Indicate an error condition - } + public int getFixMethodWithSub(Subrecord rec) { + return fixData & 0x04 | rec.getThreadMethod() & 0x3; } - public void resolveTarget(FixupState state) throws OmfException { - int method; - int index; - if (isTargetThread()) { - int threadnum = fixData & 3; - ThreadSubrecord subrec = state.targetThreads[threadnum]; - method = getP(); // Most significant bit is frame fixup subrecord - method <<= 2; - method |= subrec.getMethod(); // Least significant 2 bits are from the thread - index = subrec.getIndex(); - } - else { - method = fixData & 7; - index = targetDatum; - } - - switch (method) { - case 0: // Index is for a segment - state.targetState = state.header.resolveSegment(index).getStartAddress(); - state.targetState += targetDisplacement; - break; - case 1: // Index is for a group - state.targetState = state.groups.get(index - 1).getStartAddress(); - state.targetState += targetDisplacement; - break; - case 2: // Index is for an external symbol - state.targetState = state.externals.get(index - 1).getAddress().getOffset(); - state.targetState += targetDisplacement; - break; - // case 3: // Not supported by many linkers - case 4: // segment only, no displacement - state.targetState = state.header.resolveSegment(index).getStartAddress(); - break; - case 5: // group only, no displacement - state.targetState = state.groups.get(index - 1).getStartAddress(); - break; - case 6: // external only, no displacement - state.targetState = state.externals.get(index - 1).getAddress().getOffset(); - break; - default: - state.targetState = -1; // This indicates an unresolved target - } + public int getFixMethod() { + return fixData & 7; } - public static FixupTarget readFixupTarget(BinaryReader reader, boolean hasBigFields) - throws IOException { - FixupTarget fixupTarget = new FixupTarget(); - fixupTarget.fixData = reader.readNextByte(); - if ((fixupTarget.fixData & 0x80) == 0) { // F=0 (explicit frame method (and datum)) - int method = (fixupTarget.fixData >> 4) & 7; - if (method < 3) { - fixupTarget.frameDatum = OmfRecord.readIndex(reader); - } - } - if ((fixupTarget.fixData & 0x08) == 0) { // T=0 (explicit target) - fixupTarget.targetDatum = OmfRecord.readIndex(reader); - } - if ((fixupTarget.fixData & 0x04) == 0) // P=0 - fixupTarget.targetDisplacement = OmfRecord.readInt2Or4(reader, hasBigFields); - return fixupTarget; + public int getTargetDatum() { + return targetDatum; + } + + public int getTargetDisplacement() { + return targetDisplacement; + } + + public int getLocationType() { + return first >> 2 & 0xf; + } + + public int getDataRecordOffset() { + return (first & 3) << 8 | hiFixup & 0xff; + } + + public boolean isSegmentRelative() { + return (first & 0x40) != 0; } } - public static class FixupSubrecord extends Subrecord { - private byte lobyte; // lo-byte of location - private byte hibyte; // hi-byte of location - private FixupTarget target; - - public FixupSubrecord() { - super(false); - } - - public void resolveFixup(FixupState state) throws OmfException { - - target.resolveTarget(state); // Resolve target first as frame may need to reference results - target.resolveFrame(state); - state.M = ((lobyte >> 6) & 1) != 0; - state.locationType = ((lobyte >> 2) & 0xf); - int dataRecordOffset = lobyte & 3; - dataRecordOffset <<= 8; - dataRecordOffset |= (hibyte) & 0xff; - long blockDisplace; - int segIndex; - if (state.currentFixupRecord.lastLEData != null) { - blockDisplace = state.currentFixupRecord.lastLEData.getDataOffset(); - segIndex = state.currentFixupRecord.lastLEData.getSegmentIndex(); - } - else { - blockDisplace = state.currentFixupRecord.lastLIData.getDataOffset(); - segIndex = state.currentFixupRecord.lastLIData.getSegmentIndex(); - } - OmfSegmentHeader seg = state.header.resolveSegment(segIndex); - state.locAddress = seg.getAddress(state.language).add(blockDisplace + dataRecordOffset); - } - - public static FixupSubrecord readFixupSubrecord(BinaryReader reader, boolean hasBigFields) - throws IOException { - FixupSubrecord fixupSubrecord = new FixupSubrecord(); - fixupSubrecord.lobyte = reader.readNextByte(); - fixupSubrecord.hibyte = reader.readNextByte(); - fixupSubrecord.target = FixupTarget.readFixupTarget(reader, hasBigFields); - return fixupSubrecord; - } - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfIteratedData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfIteratedData.java index 99c7fb6919..df5f2b9720 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfIteratedData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfIteratedData.java @@ -20,11 +20,9 @@ import java.util.ArrayList; import ghidra.app.util.bin.BinaryReader; -public class OmfIteratedData extends OmfRecord implements OmfData { +public class OmfIteratedData extends OmfData { public static final int MAX_ITERATED_FILL = 0x100000; // Maximum number of bytes in expanded form - private int segmentIndex; - private long dataOffset; private DataBlock[] datablock; public OmfIteratedData(BinaryReader reader) throws IOException { @@ -43,15 +41,9 @@ public class OmfIteratedData extends OmfRecord implements OmfData { blocklist.toArray(datablock); } - public int getSegmentIndex() { - return segmentIndex; - } - - @Override - public long getDataOffset() { - return dataOffset; - } - + /** + * @return true if the block is all zeros + */ @Override public boolean isAllZeroes() { for (int i = 0; i < datablock.length; ++i) { @@ -62,6 +54,9 @@ public class OmfIteratedData extends OmfRecord implements OmfData { return true; } + /** + * @return the length of the block after expansion + */ @Override public int getLength() { int length = 0; @@ -71,6 +66,10 @@ public class OmfIteratedData extends OmfRecord implements OmfData { return length; } + /** + * @param reader The file to read from + * @return The block created from the read data + */ @Override public byte[] getByteArray(BinaryReader reader) throws IOException { int length = getLength(); @@ -85,15 +84,10 @@ public class OmfIteratedData extends OmfRecord implements OmfData { return buffer; } - @Override - public int compareTo(OmfData o) { - long otherOffset = o.getDataOffset(); - if (dataOffset == otherOffset) { - return 0; - } - return (dataOffset < otherOffset) ? -1 : 1; - } - + /** + * Contain the definition of one part of a datablock with possible recursion + * + */ public static class DataBlock { private int repeatCount; private int blockCount; @@ -120,6 +114,12 @@ public class OmfIteratedData extends OmfRecord implements OmfData { return subblock; } + /** + * Fill part of the buffer + * @param buffer The buffer to fill + * @param pos The next position to fill + * @return The position after the block + */ public int fillBuffer(byte[] buffer, int pos) { for (int i = 0; i < repeatCount; ++i) { if (simpleBlock != null) { @@ -137,6 +137,9 @@ public class OmfIteratedData extends OmfRecord implements OmfData { return pos; } + /** + * @return The length of this block + */ public int getLength() { int length = 0; if (simpleBlock != null) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfModuleEnd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfModuleEnd.java index a5def4055f..743c3c9cd7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfModuleEnd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfModuleEnd.java @@ -1,4 +1,5 @@ /* ### +, * IP: GHIDRA * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,18 +21,24 @@ import java.io.IOException; import ghidra.app.util.bin.BinaryReader; public class OmfModuleEnd extends OmfRecord { - private byte moduleType; - private OmfFixupRecord.FixupTarget startAddress; + //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()) { - startAddress = OmfFixupRecord.FixupTarget.readFixupTarget(reader, hasBigFields()); + 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); } @@ -39,4 +46,5 @@ public class OmfModuleEnd extends OmfRecord { public boolean hasStartAddress() { return ((moduleType & 0x40) != 0); } +*/ } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfSymbolRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfSymbolRecord.java index e1bb3c33f8..323befa771 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfSymbolRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfSymbolRecord.java @@ -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; @@ -70,4 +71,9 @@ public class OmfSymbolRecord extends OmfRecord { public OmfSymbol getSymbol(int i) { return symbol[i]; } + + public List getSymbols() { + return List.of(symbol); + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java index 596b9efaa5..8bec09d2a9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java @@ -17,6 +17,7 @@ package ghidra.app.util.opinion; import java.io.IOException; import java.util.*; +import java.util.stream.Collectors; import ghidra.app.util.MemoryBlockUtils; import ghidra.app.util.Option; @@ -46,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 externsyms = null; + private ArrayList externsyms = new ArrayList<>(); /** * OMF usually stores a string describing the compiler that produced it in a @@ -134,8 +135,8 @@ public class OmfLoader extends AbstractProgramWrapperLoader { try { processSegmentHeaders(reader, header, program, monitor, log); - processExternalSymbols(header, program, monitor, log); processPublicSymbols(header, program, monitor, log); + processExternalSymbols(header, program, monitor, log); processRelocations(header, program, monitor, log); } catch (AddressOverflowException e) { @@ -149,13 +150,13 @@ public class OmfLoader extends AbstractProgramWrapperLoader { * @param log will receive the error message * @param state is the relocation record that could not be processed */ - private void relocationError(Program program, MessageLog log, OmfFixupRecord.FixupState state) { + private void relocationError(Program program, MessageLog log, Address addr, int type) { String message; - if (state.locAddress != null) { - message = "Unable to process relocation at " + state.locAddress + " with type 0x" + - Integer.toHexString(state.locationType); - program.getBookmarkManager().setBookmark(state.locAddress, BookmarkType.ERROR, - "Relocations", message); + if (addr != null) { + message = "Unable to process relocation at " + addr + " with type 0x" + + Integer.toHexString(type); + program.getBookmarkManager() + .setBookmark(addr, BookmarkType.ERROR, "Relocations", message); } else { message = "Badly broken relocation"; @@ -172,100 +173,163 @@ public class OmfLoader extends AbstractProgramWrapperLoader { */ private void processRelocations(OmfFileHeader header, Program program, TaskMonitor monitor, MessageLog log) { - ArrayList fixups = header.getFixups(); - OmfFixupRecord.FixupState state = - new OmfFixupRecord.FixupState(header, externsyms, program.getLanguage()); + Language language = program.getLanguage(); +// OmfFixupRecord.Subrecord[] frameThreads = new Subrecord[4]; + OmfFixupRecord.Subrecord[] targetThreads = new Subrecord[4]; + ArrayList 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()); - for (OmfFixupRecord fixup : fixups) { - state.currentFixupRecord = fixup; - Subrecord[] subrecs = fixup.getSubrecords(); - Memory memory = program.getMemory(); - for (Subrecord subrec : subrecs) { + monitor.setMessage("Process relocations..."); + Memory memory = program.getMemory(); + for (OmfFixupRecord fixup : header.getFixups()) { + for (Subrecord subrec : fixup.getSubrecords()) { if (monitor.isCancelled()) { break; } - - if (subrec.isThread()) { - ((OmfFixupRecord.ThreadSubrecord) subrec).updateState(state); + if (subrec.isThreadSubrecord()) { + if (!subrec.isFrameInSubThread()) { + targetThreads[subrec.getThreadNum()] = subrec; + } } else { long finalvalue = -1; byte[] origbytes = null; + int method, index, locationType = -1; + locAddress = null; try { - OmfFixupRecord.FixupSubrecord fixsub = - (OmfFixupRecord.FixupSubrecord) subrec; - state.clear(); - fixsub.resolveFixup(state); - if (state.targetState == -1 || state.locAddress == null) { - relocationError(program, log, state); + if (subrec.isTargetThread()) { + Subrecord rec = targetThreads[subrec.getFixThreadNum()]; + method = subrec.getFixMethodWithSub(rec); + index = rec.getIndex(); + } + else { + method = subrec.getFixMethod(); + index = subrec.getTargetDatum(); + } + switch (method) { + case 0: // Index is for a segment + case 4: // segment only, no displacement + targetAddr = header.resolveSegment(index).getStartAddress(); + break; + case 1: // Index is for a group + case 5: // group only, no displacement + targetAddr = groups.get(index - 1).getStartAddress(); + break; + case 2: // Index is for an external symbol + case 6: // external only, no displacement + OmfSymbol symbol = externsyms.get(index - 1); + targetAddr = symbol.getAddress().getOffset(); + break; + case 3: // Not supported by many linkers + default: + log.appendMsg( + "Unsupported target method " + Integer.toString(method)); + continue; + } + if (method < 3) + targetAddr += subrec.getTargetDisplacement(); + locationType = subrec.getLocationType(); + OmfSegmentHeader seg = + header.resolveSegment(fixup.getDataBlock().getSegmentIndex()); + locAddress = seg.getAddress(language) + .add( + fixup.getDataBlock().getDataOffset() + + subrec.getDataRecordOffset()); + if (locAddress == null) { + log.appendMsg("Couldn't find address for fixup"); continue; } - - switch (state.locationType) { - case 0: // Low-order byte - origbytes = new byte[1]; - memory.getBytes(state.locAddress, origbytes); - finalvalue = state.targetState; - if (state.M) { - finalvalue += origbytes[0]; - } - else { - finalvalue -= (state.locAddress.getOffset() + 1); - } - memory.setByte(state.locAddress, (byte) finalvalue); - break; - case 1: // 16-bit offset - case 5: // 16-bit loader-resolved offset (treated same as 1) - origbytes = new byte[2]; - memory.getBytes(state.locAddress, origbytes); - finalvalue = state.targetState; - if (state.M) { - finalvalue += converter.getShort(origbytes); - } - else { - finalvalue -= (state.locAddress.getOffset() + 2); - } - memory.setShort(state.locAddress, (short) finalvalue); - break; - // case 2: // 16-bit base -- logical segment base (selector) - // case 3: // 32-bit Long pointer (16-bit base:16-bit offset - case 4: // High-order byte (high byte of 16-bit offset) - case 9: // 32-bit offset - case 13: // 32-bit loader-resolved offset (treated same as 9) - origbytes = new byte[4]; - memory.getBytes(state.locAddress, origbytes); - finalvalue = state.targetState; - if (state.M) { - finalvalue += converter.getInt(origbytes); - } - else { - finalvalue -= (state.locAddress.getOffset() + 4); - } - memory.setInt(state.locAddress, (int) finalvalue); - break; - // case 11: // 48-bit pointer (16-bit base:32-bit offset) - default: - log.appendMsg("Unsupported relocation type " + - Integer.toString(state.locationType) + " at 0x" + - Long.toHexString(state.locAddress.getOffset())); - break; + finalvalue = targetAddr; + switch (locationType) { + case 0: // Low-order byte + origbytes = new byte[1]; + memory.getBytes(locAddress, origbytes); + if (subrec.isSegmentRelative()) { + finalvalue += origbytes[0]; + } + else { + finalvalue -= (locAddress.getOffset() + 1); + } + memory.setByte(locAddress, (byte) finalvalue); + break; + case 1: // 16-bit offset + case 5: // 16-bit loader-resolved offset (treated same as 1) + origbytes = new byte[2]; + memory.getBytes(locAddress, origbytes); + if (subrec.isSegmentRelative()) { + finalvalue += converter.getShort(origbytes); + } + else { + finalvalue -= (locAddress.getOffset() + 2); + } + memory.setShort(locAddress, (short) finalvalue); + break; + case 2: // 16-bit base -- logical segment base (selector) + if (!subrec.isSegmentRelative()) { + // Segment can't be self relative + relocationError(program, log, locAddress, locationType); + continue; + } + origbytes = new byte[2]; + memory.getBytes(locAddress, origbytes); + finalvalue += converter.getShort(origbytes) << 4; + finalvalue >>= 4; // Convert address to segment + memory.setShort(locAddress, (short) finalvalue); + break; + case 3: // 32-bit far pointer (16-bit segment:16-bit offset) + if (!subrec.isSegmentRelative()) { + // Far can't be self relative + relocationError(program, log, locAddress, locationType); + continue; + } + origbytes = new byte[4]; + memory.getBytes(locAddress, origbytes); + finalvalue += converter.getInt(origbytes); + // Convert to segment:offset in 64K blocks + finalvalue = + ((finalvalue & 0xffff0000L) << 12) | (finalvalue & 0xffff); + memory.setInt(locAddress, (int) finalvalue); + break; + // case 11: // 48-bit far pointer (16-bit segment:32-bit offset) + case 4: // High-order byte (high byte of 16-bit offset) + case 9: // 32-bit offset + case 13: // 32-bit loader-resolved offset (treated same as 9) + origbytes = new byte[4]; + memory.getBytes(locAddress, origbytes); + if (subrec.isSegmentRelative()) { + finalvalue += converter.getInt(origbytes); + } + else { + finalvalue -= (locAddress.getOffset() + 4); + } + memory.setInt(locAddress, (int) finalvalue); + break; + default: + log.appendMsg("Unsupported relocation type " + + Integer.toString(locationType) + " at 0x" + + Long.toHexString(locAddress.getOffset())); + break; } } catch (MemoryAccessException e) { - relocationError(program, log, state); + relocationError(program, log, locAddress, locationType); continue; } catch (OmfException e) { - relocationError(program, log, state); + relocationError(program, log, locAddress, locationType); + continue; + } + catch (IndexOutOfBoundsException e) { + relocationError(program, log, locAddress, locationType); continue; } long[] values = new long[1]; values[0] = finalvalue; program.getRelocationTable() - .add(state.locAddress, Status.APPLIED, - state.locationType, values, origbytes, null); + .add(locAddress, Status.APPLIED, locationType, values, origbytes, null); } } } @@ -493,11 +557,14 @@ public class OmfLoader extends AbstractProgramWrapperLoader { return; } Address externalAddressStart = externalAddress; - externsyms = new ArrayList<>(); - SymbolTable symbolTable = program.getSymbolTable(); Language language = program.getLanguage(); + Map publicSymbols = header.getPublicSymbols() + .stream() + .flatMap(symbolRec -> symbolRec.getSymbols().stream()) + .collect(Collectors.toMap(sym -> sym.getName(), java.util.function.Function.identity())); + monitor.setMessage("Creating External Symbols"); for (OmfExternalSymbol symbolrec : symbolrecs) { @@ -506,6 +573,12 @@ public class OmfLoader extends AbstractProgramWrapperLoader { if (monitor.isCancelled()) { break; } + OmfSymbol public_symbol = publicSymbols.get(symbol.getName()); + if (public_symbol != null) { + // Use existing public symbol + externsyms.add(public_symbol); + continue; + } Address address = null; if (symbol.getSegmentRef() != 0) { // Look for special Borland segment symbols OmfSegmentHeader segment = From fb3790138e7a1c80f96b22120ad45005cecf4f83 Mon Sep 17 00:00:00 2001 From: Gravelbones Date: Thu, 23 Feb 2023 20:08:02 +0100 Subject: [PATCH 2/5] Fix problem introduced by #5016 --- .../java/ghidra/app/util/bin/format/omf/OmfCommentRecord.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfCommentRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfCommentRecord.java index 1f840b8fec..e93d4def4a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfCommentRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfCommentRecord.java @@ -47,6 +47,9 @@ public class OmfCommentRecord extends OmfRecord { case COMMENT_CLASS_LIBMOD: value = readString(reader); break; + default: + reader.setPointerIndex(reader.getPointerIndex() + getRecordLength() - 3); + break; } readCheckSumByte(reader); } From 8040c08250a6ac00c225181aea819e72b1590888 Mon Sep 17 00:00:00 2001 From: Gravelbones Date: Thu, 23 Feb 2023 20:10:30 +0100 Subject: [PATCH 3/5] Handle Floating Point special names in relocation --- .../ghidra/app/util/bin/format/omf/OmfSymbol.java | 15 +++++++++++++++ .../java/ghidra/app/util/opinion/OmfLoader.java | 3 +++ 2 files changed, 18 insertions(+) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfSymbol.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfSymbol.java index 2a2f00c911..9c8b771c10 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfSymbol.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfSymbol.java @@ -15,6 +15,8 @@ */ package ghidra.app.util.bin.format.omf; +import java.util.Set; + import ghidra.program.model.address.Address; public class OmfSymbol { @@ -26,6 +28,15 @@ public class OmfSymbol { private long offset; private Address address; + /** + * These names were taken from the OpenWatcom source code + * https://github.com/open-watcom/open-watcom-v2/blob/master/bld/watcom/h/fppatche.h + */ + private static final Set FLOATINGPOINT_SPECIALNAMES = Set.of( + "FIWRQQ", "FIDRQQ", "FIERQQ", "FICRQQ", "FJCRQQ", "FISRQQ", + "FJSRQQ", "FIARQQ", "FJARQQ", "FIFRQQ", "FJFRQQ", "FIGRQQ", + "FJGRQQ"); + public OmfSymbol(String name, int type, long off, int dT, int bL) { symbolName = name; typeIndex = type; @@ -65,4 +76,8 @@ public class OmfSymbol { public int getFrameDatum() { return 0; // This is currently unused } + + public boolean isFloatingPointSpecial() { + return FLOATINGPOINT_SPECIALNAMES.contains(symbolName); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java index 8bec09d2a9..95a286d26f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java @@ -221,6 +221,9 @@ public class OmfLoader extends AbstractProgramWrapperLoader { case 2: // Index is for an external symbol case 6: // external only, no displacement OmfSymbol symbol = externsyms.get(index - 1); + if (symbol.isFloatingPointSpecial()) { + continue; + } targetAddr = symbol.getAddress().getOffset(); break; case 3: // Not supported by many linkers From bc5787a557c437e7209c96b4dc0805c96c0f821d Mon Sep 17 00:00:00 2001 From: Gravelbones Date: Thu, 23 Feb 2023 20:35:17 +0100 Subject: [PATCH 4/5] Fix error from #5016 --- .../src/main/java/ghidra/app/util/bin/format/omf/OmfRecord.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 cbabc44114..596b276d04 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 @@ -247,6 +247,6 @@ public abstract class OmfRecord { @Override public String toString() { return String.format("name: %s, type: 0x%x, offset: 0x%x, length: 0x%x", - getRecordName(recordType & 0xfe), recordType, recordOffset, recordLength); + getRecordName(recordType & (byte)0xfe), recordType, recordOffset, recordLength); } } From 7728c7b55e81ca4305530186a8eabf343103fd0e Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Mon, 27 Feb 2023 09:15:24 -0500 Subject: [PATCH 5/5] GP-3141: Code cleanup and certify --- .../java/ghidra/app/util/bin/format/omf/OmfData.java | 9 +++++---- .../app/util/bin/format/omf/OmfEnumeratedData.java | 11 ----------- .../app/util/bin/format/omf/OmfFixupRecord.java | 4 ++-- .../app/util/bin/format/omf/OmfIteratedData.java | 11 ----------- .../ghidra/app/util/bin/format/omf/OmfModuleEnd.java | 1 - .../ghidra/app/util/bin/format/omf/OmfRecord.java | 2 +- .../main/java/ghidra/app/util/opinion/OmfLoader.java | 3 +-- 7 files changed, 9 insertions(+), 32 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfData.java index 5432199284..0b0e827e19 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfData.java @@ -42,7 +42,8 @@ public abstract class OmfData extends OmfRecord implements Comparable { /** * Compare datablocks by data offset - * @return -1 for lower address, 0 for same adress, 1 for higher address + * @return a value less than 0 for lower address, 0 for same address, or greater than 0 for + * higher address */ @Override public int compareTo(OmfData o) { @@ -52,7 +53,7 @@ public abstract class OmfData extends OmfRecord implements Comparable { /** * @return the length of this data in bytes */ - abstract public int getLength(); + public abstract int getLength(); /** * Create a byte array holding the data represented by this object. The length @@ -61,10 +62,10 @@ public abstract class OmfData extends OmfRecord implements Comparable { * @return allocated and filled byte array * @throws IOException for problems accessing data through the reader */ - abstract public byte[] getByteArray(BinaryReader reader) throws IOException; + public abstract byte[] getByteArray(BinaryReader reader) throws IOException; /** * @return true if this is a block entirely of zeroes */ - abstract public boolean isAllZeroes(); + public abstract boolean isAllZeroes(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfEnumeratedData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfEnumeratedData.java index 13abdcbd63..d045464425 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfEnumeratedData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfEnumeratedData.java @@ -34,18 +34,11 @@ public class OmfEnumeratedData extends OmfData { readCheckSumByte(reader); } - /** - * @return The length of the block - */ @Override public int getLength() { return streamLength; } - /** - * @param The inputfile to read from - * @return The array of bytes - */ @Override public byte[] getByteArray(BinaryReader reader) throws IOException { reader.setPointerIndex(streamOffset); @@ -53,10 +46,6 @@ public class OmfEnumeratedData extends OmfData { return buffer; } - /** - * Assume that all zeros are done with OmfIteratedData - * @return True if the block is all zeroes - */ @Override public boolean isAllZeroes() { return false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFixupRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFixupRecord.java index 7575942dfc..1b19702861 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFixupRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfFixupRecord.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfIteratedData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfIteratedData.java index df5f2b9720..0ebd4bf539 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfIteratedData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfIteratedData.java @@ -41,9 +41,6 @@ public class OmfIteratedData extends OmfData { blocklist.toArray(datablock); } - /** - * @return true if the block is all zeros - */ @Override public boolean isAllZeroes() { for (int i = 0; i < datablock.length; ++i) { @@ -54,9 +51,6 @@ public class OmfIteratedData extends OmfData { return true; } - /** - * @return the length of the block after expansion - */ @Override public int getLength() { int length = 0; @@ -66,10 +60,6 @@ public class OmfIteratedData extends OmfData { return length; } - /** - * @param reader The file to read from - * @return The block created from the read data - */ @Override public byte[] getByteArray(BinaryReader reader) throws IOException { int length = getLength(); @@ -86,7 +76,6 @@ public class OmfIteratedData extends OmfData { /** * Contain the definition of one part of a datablock with possible recursion - * */ public static class DataBlock { private int repeatCount; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfModuleEnd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfModuleEnd.java index 743c3c9cd7..55de5ead15 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfModuleEnd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/OmfModuleEnd.java @@ -1,5 +1,4 @@ /* ### -, * IP: GHIDRA * * Licensed under the Apache License, Version 2.0 (the "License"); 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 596b276d04..f2d79f6291 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 @@ -247,6 +247,6 @@ public abstract class OmfRecord { @Override public String toString() { return String.format("name: %s, type: 0x%x, offset: 0x%x, length: 0x%x", - getRecordName(recordType & (byte)0xfe), recordType, recordOffset, recordLength); + getRecordName(recordType & (byte) 0xfe), recordType, recordOffset, recordLength); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java index 95a286d26f..cf3a038746 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java @@ -148,7 +148,7 @@ public class OmfLoader extends AbstractProgramWrapperLoader { * Log a (hopefully) descriptive error, if we can't process a specific relocation * @param program is the Program * @param log will receive the error message - * @param state is the relocation record that could not be processed + * @param type the relocation type */ private void relocationError(Program program, MessageLog log, Address addr, int type) { String message; @@ -174,7 +174,6 @@ public class OmfLoader extends AbstractProgramWrapperLoader { private void processRelocations(OmfFileHeader header, Program program, TaskMonitor monitor, MessageLog log) { Language language = program.getLanguage(); -// OmfFixupRecord.Subrecord[] frameThreads = new Subrecord[4]; OmfFixupRecord.Subrecord[] targetThreads = new Subrecord[4]; ArrayList groups = header.getGroups(); long targetAddr; // Address of item being referred to