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 df55edc614..52371a11ba 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 @@ -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. @@ -20,6 +20,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; import ghidra.app.util.bin.BinaryReader; import ghidra.program.model.data.*; @@ -132,4 +133,18 @@ public class OmfUtils { return records; } + + /** + * Returns a {@link Stream} of {@link OmfRecord records} that match the given class type + * + * @param The class type + * @param records The {@link List} of all {@link OmfRecord records} + * @param classType The class type to match on + * @return A {@link Stream} of matching (@link OmfRecord records} + */ + public static Stream filterRecords(List records, Class classType) { + return records.stream() + .filter(classType::isInstance) + .map(classType::cast); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Content.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Content.java index 78fe1434e1..6a7dd8f7b9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Content.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Content.java @@ -24,31 +24,38 @@ import ghidra.util.exception.DuplicateNameException; public class Omf51Content extends OmfRecord { - private byte segId; + private int segId; private int offset; - private byte[] dataBytes; + private long dataIndex; + private int dataSize; + + boolean largeSegmentId; /** * Creates a new {@link Omf51Content} record * * @param reader A {@link BinaryReader} positioned at the start of the record + * @param largeSegmentId True if the segment ID is 2 bytes; false if 1 byte * @throws IOException if an IO-related error occurred */ - public Omf51Content(BinaryReader reader) throws IOException { + public Omf51Content(BinaryReader reader, boolean largeSegmentId) throws IOException { super(reader); + this.largeSegmentId = largeSegmentId; } @Override public void parseData() throws IOException, OmfException { - segId = dataReader.readNextByte(); + segId = + largeSegmentId ? dataReader.readNextUnsignedShort() : dataReader.readNextUnsignedByte(); offset = dataReader.readNextUnsignedShort(); - dataBytes = dataReader.readNextByteArray((int) (dataEnd - dataReader.getPointerIndex())); + dataIndex = dataReader.getPointerIndex(); + dataSize = (int) (dataEnd - dataIndex); } /** * {@return the segment ID} */ - public byte getSegId() { + public int getSegId() { return segId; } @@ -60,10 +67,17 @@ public class Omf51Content extends OmfRecord { } /** - * {@return the data} + * {@return the data size in bytes} */ - public byte[] getDataBytes() { - return dataBytes; + public int getDataSize() { + return dataSize; + } + + /** + * {@return the start of the data in the reader} + */ + public long getDataIndex() { + return dataIndex; } @Override @@ -71,10 +85,10 @@ public class Omf51Content extends OmfRecord { StructureDataType struct = new StructureDataType(Omf51RecordTypes.getName(recordType), 0); struct.add(BYTE, "type", null); struct.add(WORD, "length", null); - struct.add(BYTE, "SEG ID", null); + struct.add(largeSegmentId ? WORD : BYTE, "SEG ID", null); struct.add(WORD, "offset", null); - if (dataBytes.length > 0) { - struct.add(new ArrayDataType(BYTE, dataBytes.length, 1), "data", null); + if (dataSize > 0) { + struct.add(new ArrayDataType(BYTE, dataSize, 1), "data", null); } struct.add(BYTE, "checksum", null); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51FixupRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51FixupRecord.java new file mode 100644 index 0000000000..2b06d10c97 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51FixupRecord.java @@ -0,0 +1,85 @@ +/* ### + * 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 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 Omf51FixupRecord extends OmfRecord { + + /** + * OMF-51 fixup metadata + * + * @param refLoc The reference location + * @param refType The reference type + * @param operand the fixup operand + */ + public static record Omf51Fixup(int refLoc, byte refType, int operand) {} + + private List fixups = new ArrayList<>(); + + /** + * Creates a new {@link Omf51FixupRecord} record + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @throws IOException if an IO-related error occurred + */ + public Omf51FixupRecord(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + while (dataReader.getPointerIndex() < dataEnd) { + int refLoc = dataReader.readNextUnsignedByte(); + byte refType = dataReader.readNextByte(); + int operand = dataReader.readNextUnsignedShort(); + fixups.add(new Omf51Fixup(refLoc, refType, operand)); + } + } + + @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); + StructureDataType fixupStruct = new StructureDataType("Omf51Fixup", 0); + fixupStruct.add(BYTE, "ref_loc", null); + fixupStruct.add(BYTE, "ref_type", null); + fixupStruct.add(WORD, "operand", null); + struct.add(new ArrayDataType(fixupStruct, fixups.size(), fixupStruct.getLength()), "fixup", + null); + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } + + /** + * Gets a {@link List} of fixups + * + * @return A {@link List} of fixups + */ + public List getFixups() { + return fixups; + } +} 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 index 333568245a..0a8cbbd0ce 100644 --- 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 @@ -23,8 +23,8 @@ 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; +import ghidra.app.util.bin.format.omf.omf166.Omf166RecordTypes; /** * A class for reading/creating OMF-51 records @@ -51,9 +51,15 @@ public class Omf51RecordFactory extends AbstractOmfRecordFactory { case Omf166RecordTypes.DEPLST: yield new Omf166DepList(reader); case Content: - yield new Omf51Content(reader); - case Fixup: + case KeilContent: + yield new Omf51Content(reader, true); case SegmentDEF: + yield new Omf51SegmentDefs(reader, false); + case KeilSegmentDEF: + yield new Omf51SegmentDefs(reader, true); + case KeilFixup: + yield new Omf51FixupRecord(reader); + case Fixup: case ScopeDEF: case DebugItem: case PublicDEF: 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 index 6407a3ad4a..0159a6ac52 100644 --- 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 @@ -40,6 +40,8 @@ public class Omf51RecordTypes { // Record types with names ending in "Keil", which are produced by ARM Keil's // 8051 tooling, are only slight variants of the similarly-named record types in the Intel spec. + public final static int KeilContent = Content + 1; + public final static int KeilFixup = Fixup + 1; public final static int KeilSegmentDEF = SegmentDEF + 1; public final static int KeilScopeDEF = ScopeDEF + 1; public final static int KeilDebugItemOBJ = 0x22; // Keil debug items, in linker output format diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Segment.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Segment.java new file mode 100644 index 0000000000..57c8429a25 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Segment.java @@ -0,0 +1,137 @@ +/* ### + * 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.OmfString; +import ghidra.app.util.bin.format.omf.OmfUtils; + +public class Omf51Segment { + + // Segment Types + public static final int CODE = 0; + public static final int XDATA = 1; + public static final int DATA = 2; + public static final int IDATA = 3; + public static final int BIT = 4; + + // Relocation Types + public static final int ABS = 0; + public static final int UNIT = 1; + public static final int BITADDRESSABLE = 2; + public static final int INPAGE = 3; + public static final int INBLOCK = 4; + public static final int PAGE = 5; + + private int id; + private byte info; + private byte relType; + private int base; + private int size; + private OmfString name; + + /** + * Creates a new {@link Omf51Segment} + * + * @param reader A {@link BinaryReader} positioned at the start of the segment definition + * @param largeSegmentId True if the segment ID is 2 bytes; false if 1 byte + * @throws IOException if an IO-related error occurred + */ + public Omf51Segment(BinaryReader reader, boolean largeSegmentId) throws IOException { + id = largeSegmentId ? reader.readNextUnsignedShort() : reader.readNextUnsignedByte(); + info = reader.readNextByte(); + relType = reader.readNextByte(); + reader.readNextByte(); // unused + base = reader.readNextUnsignedShort(); + size = reader.readNextUnsignedShort(); + name = OmfUtils.readString(reader); + + // Size of 0 is used to represent 0x10000 + if (size == 0) { + size = 0x10000; + } + + // Size is ignored if empty bit is set, but force it to be 0 for consistency + if ((info & 0x8) != 0) { + size = 0; + } + } + + /** + * {@return the segment id} + */ + public int id() { + return id; + } + + /** + * {@return the segment info} + */ + public byte info() { + return info; + } + + /** + * {@return the segment relocation type} + */ + public byte relType() { + return relType; + } + + /** + * {@return the segment base address} + */ + public int base() { + return base; + } + + /** + * {@return the segment size} + */ + public int size() { + return size; + } + + /** + * {@return the segment name} + */ + public OmfString name() { + return name; + } + + /** + * {@return the segment type (CODE, XDATA, etc)} + */ + public int getType() { + return info & 7; + } + + /** + * {@return whether or not this segment is code} + */ + public boolean isCode() { + return getType() == CODE; + } + + /** + * {@return whether or not this segment is absolute} + */ + public boolean isAbsolute() { + return id == 0; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51SegmentDefs.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51SegmentDefs.java new file mode 100644 index 0000000000..dfbc26b9f5 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51SegmentDefs.java @@ -0,0 +1,77 @@ +/* ### + * 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 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 Omf51SegmentDefs extends OmfRecord { + + private boolean largeSegmentId; + private List segments = new ArrayList<>(); + + /** + * Creates a new {@link Omf51SegmentDefs} record + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @param largeSegmentId True if the segment ID is 2 bytes; false if 1 byte + * @throws IOException if an IO-related error occurred + */ + public Omf51SegmentDefs(BinaryReader reader, boolean largeSegmentId) throws IOException { + super(reader); + this.largeSegmentId = largeSegmentId; + } + + @Override + public void parseData() throws IOException, OmfException { + while (dataReader.getPointerIndex() < dataEnd) { + segments.add(new Omf51Segment(dataReader, largeSegmentId)); + } + } + + @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); + for (Omf51Segment segment : segments) { + struct.add(largeSegmentId ? WORD : BYTE, "id", null); + struct.add(BYTE, "info", null); + struct.add(BYTE, "rel type", null); + struct.add(BYTE, "unused", null); + struct.add(WORD, "base", null); + struct.add(WORD, "size", null); + struct.add(segment.name().toDataType(), segment.name().getDataTypeSize(), "name", null); + } + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } + + /** + * {@return the list of segments} + */ + public List getSegments() { + return segments; + } +} 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 index 75c235862a..cfd334be82 100644 --- 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 @@ -17,21 +17,23 @@ 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; import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.StructConverter; import ghidra.app.util.bin.format.omf.*; -import ghidra.app.util.bin.format.omf.omf51.Omf51RecordFactory; +import ghidra.app.util.bin.format.omf.omf51.*; +import ghidra.app.util.bin.format.omf.omf51.Omf51FixupRecord.Omf51Fixup; 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.address.*; import ghidra.program.model.data.DataUtilities; import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.model.reloc.Relocation.Status; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -79,13 +81,114 @@ public class Omf51Loader extends AbstractProgramWrapperLoader { AbstractOmfRecordFactory factory = new Omf51RecordFactory(provider); try { List records = OmfUtils.readRecords(factory); + Map segmentToAddr = + processMemoryBlocks(program, fileBytes, records, log, monitor); + performFixups(program, fileBytes, records, segmentToAddr, log, monitor); markupRecords(program, fileBytes, records, log, monitor); } - catch (OmfException e) { + catch (Exception e) { throw new IOException(e); } } + private Map processMemoryBlocks(Program program, FileBytes fileBytes, + List records, MessageLog log, TaskMonitor monitor) throws Exception { + + // Gather all segments for processing, putting the absolute segments (id == 0) first since + // they are not flexible about where they get placed + List segments = OmfUtils.filterRecords(records, Omf51SegmentDefs.class) + .map(Omf51SegmentDefs::getSegments) + .flatMap(List::stream) + .sorted((a, b) -> Integer.compare(a.id(), b.id())) // absolute (id=0) comes first + .toList(); + + // Group all of a segment's content records together + Map> contentMap = + OmfUtils.filterRecords(records, Omf51Content.class) + .collect(Collectors.groupingBy(Omf51Content::getSegId)); + + // Create some data structures that will aid in segment relocation: + // - A set of addresses currently in use, so we can find holes for new segments in the + // address space + // - A map to keep track of segment's size, since segments with different ID's but the + // same name and type must be adjacent + // - A map to keep track of where same-named segments currently ends, so the next + // same-named segment can easily know where to go + // NOTE: The key for these maps needs to include both the segment name and the type + AddressSet usedAddresses = new AddressSet(); + Map segmentSizes = new HashMap<>(); + Map segmentEnds = new HashMap<>(); + for (Omf51Segment segment : segments) { + segmentSizes.compute(key(segment), + (k, v) -> (v == null ? segment.size() : v + segment.size())); + } + + // We will be returning a map of segment ID's to starting address, for use when we later + // perform fixups within the content + Map segmentToAddr = new HashMap<>(); + + for (Omf51Segment segment : segments) { + List segmentContent = contentMap.get(segment.id()); + String blockName = segment.isAbsolute() ? "" : segment.name().str(); + if (blockName.isBlank()) { + blockName = ""; + } + AddressSpace space = getAddressSpace(program, segment); + Address segmentAddr; + if (segmentContent != null) { + segmentAddr = findAddr(segment, segmentSizes, segmentEnds, space, usedAddresses); + for (Omf51Content content : segmentContent) { + Address contentAddr = + segment.isAbsolute() ? space.getAddress(content.getOffset()) + : segmentAddr.add(content.getOffset()); + try { + MemoryBlockUtils.createInitializedBlock(program, false, blockName, + contentAddr, fileBytes, content.getDataIndex(), content.getDataSize(), + "", space.getName(), true, !segment.isCode(), segment.isCode(), log); + } + catch (Exception e) { + log.appendMsg(e.getMessage()); + } + } + } + else { + segmentAddr = findAddr(segment, segmentSizes, segmentEnds, space, usedAddresses); + MemoryBlockUtils.createUninitializedBlock(program, false, blockName, segmentAddr, + segment.size(), "", space.getName(), true, true, false, log); + } + if (segment.isCode()) { + AbstractProgramLoader.markAsFunction(program, blockName, segmentAddr); + } + segmentToAddr.put(segment.id(), segmentAddr); + } + return segmentToAddr; + } + + private void performFixups(Program program, FileBytes fileBytes, List records, + Map segmentToAddr, MessageLog log, TaskMonitor monitor) + throws Exception { + OmfRecord previous = null; + for (OmfRecord record : records) { + if (record instanceof Omf51FixupRecord fixupRec) { + if (!(previous instanceof Omf51Content content)) { + throw new Exception("Record prior to fixup is not content!"); + } + Address segmentAddr = segmentToAddr.get(content.getSegId()); + if (segmentAddr == null) { + throw new Exception("Failed to get lookup segment ID 0x%x for content fixup!" + .formatted(content.getSegId())); + } + for (Omf51Fixup fixup : fixupRec.getFixups()) { + Address fixupAddr = segmentAddr.add(fixup.refLoc()); + program.getRelocationTable() + .add(fixupAddr, Status.UNSUPPORTED, fixup.refType(), + new long[] { fixup.operand() }, 0, null); + } + } + previous = record; + } + } + private void markupRecords(Program program, FileBytes fileBytes, List records, MessageLog log, TaskMonitor monitor) { monitor.setMessage("Marking up records..."); @@ -114,6 +217,71 @@ public class Omf51Loader extends AbstractProgramWrapperLoader { } } + private Address findAddr(Omf51Segment segment, Map segmentSizes, + Map segmentEnds, AddressSpace space, AddressSet usedAddresses) + throws Exception { + return switch (segment.relType()) { + case Omf51Segment.ABS: { + if (segment.id() != 0) { + throw new Exception("Absolute segment does not have ID 0!"); + } + Address start = space.getAddress(segment.base()); + Address end = start.add(segment.size()); + if (usedAddresses.intersects(start, end)) { + throw new Exception("Absolute segment overlaps with existing segment!"); + } + usedAddresses.add(start, end); + yield start; + } + case Omf51Segment.UNIT: { + Address lastEnd = segmentEnds.get(key(segment)); + if (lastEnd != null) { + Address start = lastEnd.add(1); + Address end = start.add(segment.size() - 1); + segmentEnds.put(key(segment), end); + yield start; + } + Address start = space.getMinAddress(); + Address end = start.add(segment.size() - 1); + int requiredSize = segmentSizes.get(key(segment)); + AddressSet intersection = + usedAddresses.intersectRange(start, start.add(requiredSize - 1)); + while (!intersection.isEmpty()) { + start = intersection.getMaxAddress().add(1); + end = start.add(segment.size() - 1); + intersection = usedAddresses.intersectRange(start, start.add(requiredSize - 1)); + } + usedAddresses.add(start, start.add(requiredSize - 1)); + segmentEnds.put(key(segment), end); + yield start; + } + case Omf51Segment.BITADDRESSABLE: + case Omf51Segment.INPAGE: + case Omf51Segment.INBLOCK: + case Omf51Segment.PAGE: + default: + throw new Exception( + "Skipping segment '%s'. Relocation type 0x%x is not yet supported" + .formatted(segment.name(), segment.relType())); + }; + } + + private AddressSpace getAddressSpace(Program program, Omf51Segment segment) throws Exception { + return program.getAddressFactory().getAddressSpace(switch (segment.getType()) { + case Omf51Segment.CODE -> "CODE"; + case Omf51Segment.XDATA -> "EXTMEM"; + case Omf51Segment.DATA -> "INTMEM"; + case Omf51Segment.IDATA -> "INTMEM"; + case Omf51Segment.BIT -> "BITS"; + default -> throw new Exception( + "Unsupported address space: 0x%x".formatted(segment.getType())); + }); + } + + private String key(Omf51Segment segment) { + return segment.name().str() + "_" + segment.getType(); + } + @Override public String getName() { return OMF51_NAME;