From 64b6a358a0cb8482a966c96a1e0e7e8627fdefc9 Mon Sep 17 00:00:00 2001 From: Peter Belm Date: Thu, 27 Mar 2025 13:03:12 +0000 Subject: [PATCH 1/3] FEAT: OMF-51 Library filesystem --- .../bin/format/omf/omf51/Omf51Library.java | 110 ++++++++++++++++++ .../omf51/Omf51LibraryDictionaryRecord.java | 84 +++++++++++++ .../omf/omf51/Omf51LibraryHeaderRecord.java | 93 +++++++++++++++ .../omf/omf51/Omf51LibraryModuleLocation.java | 72 ++++++++++++ .../Omf51LibraryModuleLocationsRecord.java | 68 +++++++++++ .../omf51/Omf51LibraryModuleNamesRecord.java | 68 +++++++++++ .../format/omf/omf51/Omf51RecordFactory.java | 12 +- .../formats/omf51/Omf51ArchiveFileSystem.java | 93 +++++++++++++++ .../omf51/Omf51ArchiveFileSystemFactory.java | 73 ++++++++++++ 9 files changed, 669 insertions(+), 4 deletions(-) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Library.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryDictionaryRecord.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryHeaderRecord.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocation.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocationsRecord.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleNamesRecord.java create mode 100644 Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java create mode 100644 Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystemFactory.java diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Library.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Library.java new file mode 100644 index 0000000000..a55addbc34 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Library.java @@ -0,0 +1,110 @@ +/* ### + * 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.app.util.bin.format.omf.omf.OmfLibraryRecord.MemberHeader; +import ghidra.program.model.data.*; +import ghidra.util.Msg; +import ghidra.util.exception.DuplicateNameException; + +public class Omf51Library { + + private Omf51RecordFactory factory; + private ArrayList members = new ArrayList<>(); + + public static class MemberHeader { + public long offset; + public long size; + public String name; + } + + /** + * Creates a new {@link Omf51Library} + * + * @param reader A {@link BinaryReader} positioned at the start of the record + */ + public Omf51Library(Omf51RecordFactory factory) { + this.factory = factory; + } + + /** + * Attempts to parse OMF-51 library members + * + * @throws IOException if an IO-related error occurred + * @throws OmfException if the required OMF-51 records could not be read + */ + public void parseMembers() throws IOException, OmfException { + OmfRecord record = factory.readNextRecord(); + + if (record == null || !(record instanceof Omf51LibraryHeaderRecord)) { + throw new OmfException("Unable to read library header record"); + } + + Omf51LibraryHeaderRecord libraryHeader = (Omf51LibraryHeaderRecord)record; + + factory.getReader().setPointerIndex(libraryHeader.getModNamesOffset()); + + record = factory.readNextRecord(); + + if (record == null || !(record instanceof Omf51LibraryModuleNamesRecord)) { + throw new OmfException("Unable to read library module names record"); + } + + Omf51LibraryModuleNamesRecord modNamesRecord = (Omf51LibraryModuleNamesRecord)record; + + record = factory.readNextRecord(); + + if (record == null || !(record instanceof Omf51LibraryModuleLocationsRecord)) { + throw new OmfException("Unable to read library module locations record"); + } + + Omf51LibraryModuleLocationsRecord modLocations = (Omf51LibraryModuleLocationsRecord)record; + List locations = modLocations.getLocations(); + + int index = 0; + Msg.info(this, "Iterating mod names"); + for (OmfString moduleName : modNamesRecord.getNames()) { + int size = 0; + if (index + 1 < locations.size()) { + size = locations.get(index + 1).getOffset() - locations.get(index).getOffset(); + } else { + size = libraryHeader.getModNamesOffset() - locations.get(index).getOffset(); + } + + MemberHeader header = new MemberHeader(); + header.name = moduleName.str(); + header.size = size; + header.offset = locations.get(index).getOffset(); + + members.add(header); + + index++; + } + } + + /** + * {@return the list of members} + */ + public ArrayList getMembers() { + return members; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryDictionaryRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryDictionaryRecord.java new file mode 100644 index 0000000000..32de250a3f --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryDictionaryRecord.java @@ -0,0 +1,84 @@ +/* ### + * 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.List; +import java.util.ArrayList; + +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 Omf51LibraryDictionaryRecord extends OmfRecord { + + private List> moduleSymbolMap = new ArrayList<>(); + + /** + * Creates a new {@link Omf51LibraryDictionaryRecord} record + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @throws IOException if an IO-related error occurred + */ + public Omf51LibraryDictionaryRecord(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + List symbols = new ArrayList<>(); + + while (dataReader.getPointerIndex() < dataEnd) { + byte len = dataReader.peekNextByte(); + if (len == 0) { + dataReader.readNextByte(); + moduleSymbolMap.add(symbols); + symbols = new ArrayList<>(); + continue; + } + + symbols.add(OmfUtils.readString(dataReader)); + } + } + + @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); + Integer moduleIndex = 0; + for (List symbols : moduleSymbolMap) { + for (OmfString symbol : symbols) { + struct.add(symbol.toDataType(), symbol.getDataTypeSize(), "symbol%d".formatted(moduleIndex), null); + } + + struct.add(BYTE, "terminator%d".formatted(moduleIndex), null); + moduleIndex++; + } + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } + + /** + * {@return the symbol names partitioned by module} + */ + public List> getModuleSymbolMap() { + return moduleSymbolMap; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryHeaderRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryHeaderRecord.java new file mode 100644 index 0000000000..6cbe0f22ae --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryHeaderRecord.java @@ -0,0 +1,93 @@ +/* ### + * 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 ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.omf.*; +import ghidra.app.util.bin.format.omf.omf.OmfLibraryRecord.MemberHeader; +import ghidra.program.model.data.*; +import ghidra.util.exception.DuplicateNameException; + +public class Omf51LibraryHeaderRecord extends OmfRecord { + + public static final int BLOCK_SIZE = 128; + + private short moduleCount; + private short blockNumber; + private short byteNumber; + + /** + * Creates a new {@link Omf51LibraryHeaderRecord} record + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @throws IOException if an IO-related error occurred + */ + public Omf51LibraryHeaderRecord(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + moduleCount = dataReader.readNextShort(); + blockNumber = dataReader.readNextShort(); + byteNumber = dataReader.readNextShort(); + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType(Omf51RecordTypes.getName(recordType), 0); + struct.add(BYTE, "type", null); + struct.add(WORD, "length", null); + struct.add(WORD, "moduleCount", null); + struct.add(WORD, "blockNumber", null); + struct.add(WORD, "byteNumber", null); + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } + + /** + * {@return the module count} + */ + public short getModuleCount() { + return moduleCount; + } + + /** + * {@return the module names block number} + */ + public short getModNamesBlockNumber() { + return blockNumber; + } + + /** + * {@return the module names byte number} + */ + public short getModNamesByteNumber() { + return byteNumber; + } + + /** + * {@return the module names file offset} + */ + public int getModNamesOffset() { + return (blockNumber * BLOCK_SIZE) + byteNumber; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocation.java new file mode 100644 index 0000000000..c4a47f6d47 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocation.java @@ -0,0 +1,72 @@ +/* ### + * 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.StructConverter; +import ghidra.app.util.bin.format.omf.*; +import ghidra.program.model.data.*; +import ghidra.util.exception.DuplicateNameException; + +public class Omf51LibraryModuleLocation { + + public static final int BLOCK_SIZE = 128; + + private int blockNumber; + private int byteNumber; + + /** + * Creates a new {@link Omf51LibraryModuleLocation} + * + * @param reader A {@link BinaryReader} positioned at the start of the segment definition + * @throws IOException if an IO-related error occurred + */ + public Omf51LibraryModuleLocation(BinaryReader reader) throws IOException { + blockNumber = reader.readNextUnsignedShort(); + byteNumber = reader.readNextUnsignedShort(); + } + + /** + * {@return the block number} + */ + public int getBlockNumber() { + return blockNumber; + } + + /** + * {@return the byte number} + */ + public int getByteNumber() { + return byteNumber; + } + + /** + * {@return the offset into the library} + */ + public int getOffset() { + return (blockNumber * BLOCK_SIZE) + byteNumber; + } + + public static DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType("Omf51LibraryModuleLocation", 0); + struct.add(StructConverter.WORD, "blockNumber", null); + struct.add(StructConverter.WORD, "byteNumber", null); + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocationsRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocationsRecord.java new file mode 100644 index 0000000000..6542d44e08 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocationsRecord.java @@ -0,0 +1,68 @@ +/* ### + * 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 Omf51LibraryModuleLocationsRecord extends OmfRecord { + + private List locations = new ArrayList<>(); + + /** + * Creates a new {@link Omf51LibraryModuleLocationsRecord} record + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @throws IOException if an IO-related error occurred + */ + public Omf51LibraryModuleLocationsRecord(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + while (dataReader.getPointerIndex() < dataEnd) { + locations.add(new Omf51LibraryModuleLocation(dataReader)); + } + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType(Omf51RecordTypes.getName(recordType), 0); + struct.add(BYTE, "type", null); + struct.add(WORD, "length", null); + + struct.add(new ArrayDataType(Omf51LibraryModuleLocation.toDataType(), locations.size()), + "locations", null); + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } + + /** + * {@return the list of module locations} + */ + public List getLocations() { + return locations; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleNamesRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleNamesRecord.java new file mode 100644 index 0000000000..71f26d314c --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleNamesRecord.java @@ -0,0 +1,68 @@ +/* ### + * 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 Omf51LibraryModuleNamesRecord extends OmfRecord { + + private List names = new ArrayList<>(); + + /** + * Creates a new {@link Omf51LibraryModuleNamesRecord} record + * + * @param reader A {@link BinaryReader} positioned at the start of the record + * @throws IOException if an IO-related error occurred + */ + public Omf51LibraryModuleNamesRecord(BinaryReader reader) throws IOException { + super(reader); + } + + @Override + public void parseData() throws IOException, OmfException { + while (dataReader.getPointerIndex() < dataEnd) { + names.add(OmfUtils.readString(dataReader)); + } + } + + @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 (OmfString name : names) { + struct.add(name.toDataType(), name.getDataTypeSize(), "name", null); + } + struct.add(BYTE, "checksum", null); + + struct.setCategoryPath(new CategoryPath(OmfUtils.CATEGORY_PATH)); + return struct; + } + + /** + * {@return the list of module names} + */ + public List getNames() { + return names; + } +} 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 4bee399546..b8efb24aa3 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 @@ -70,12 +70,16 @@ public class Omf51RecordFactory extends AbstractOmfRecordFactory { yield new Omf51ExternalDefsRecord(reader, false); case KeilExternalDEF: yield new Omf51ExternalDefsRecord(reader, true); + case LibModLocs: + yield new Omf51LibraryModuleLocationsRecord(reader); + case LibModNames: + yield new Omf51LibraryModuleNamesRecord(reader); + case LibDictionary: + yield new Omf51LibraryDictionaryRecord(reader); + case LibHeader: + yield new Omf51LibraryHeaderRecord(reader); case ScopeDEF: case DebugItem: - case LibModLocs: - case LibModNames: - case LibDictionary: - case LibHeader: yield new OmfUnsupportedRecord(reader, Omf51RecordTypes.class); default: yield new OmfUnknownRecord(reader); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java new file mode 100644 index 0000000000..84167ddaf1 --- /dev/null +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java @@ -0,0 +1,93 @@ +/* ### + * 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.file.formats.omf51; + +import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*; + +import java.io.IOException; +import java.util.ArrayList; + +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.ByteProviderWrapper; +import ghidra.app.util.bin.format.omf.OmfException; +import ghidra.app.util.bin.format.omf.omf51.*; +import ghidra.formats.gfilesystem.*; +import ghidra.formats.gfilesystem.annotations.FileSystemInfo; +import ghidra.formats.gfilesystem.fileinfo.FileAttributes; +import ghidra.util.Msg; +import ghidra.util.task.TaskMonitor; + +@FileSystemInfo(type = "omf51", description = "OMF51 Library", factory = Omf51ArchiveFileSystemFactory.class) +public class Omf51ArchiveFileSystem extends AbstractFileSystem { + + private ByteProvider provider; + + public Omf51ArchiveFileSystem(FSRLRoot fsFSRL, ByteProvider provider) { + super(fsFSRL, FileSystemService.getInstance()); + this.provider = provider; + } + + public void mount(TaskMonitor monitor) throws IOException, OmfException { + Msg.debug(this, "Opening OMF51 library..."); + Omf51RecordFactory factory = new Omf51RecordFactory(provider); + Omf51Library library = new Omf51Library(factory); + library.parseMembers(); + ArrayList members = library.getMembers(); + + Msg.debug(this, "Found %d members".formatted(members.size())); + + for (Omf51Library.MemberHeader member : members) { + Msg.debug(this, member.name); + fsIndex.storeFile(member.name, fsIndex.getFileCount(), false, member.size, member); + } + } + + @Override + public void close() throws IOException { + refManager.onClose(); + if (provider != null) { + provider.close(); + provider = null; + } + fsIndex.clear(); + } + + @Override + public boolean isClosed() { + return provider == null; + } + + @Override + public ByteProvider getByteProvider(GFile file, TaskMonitor monitor) { + Omf51Library.MemberHeader member = fsIndex.getMetadata(file); + return (member != null) + ? new ByteProviderWrapper(provider, member.offset, member.size, + file.getFSRL()) + : null; + } + + @Override + public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) { + FileAttributes result = new FileAttributes(); + + Omf51Library.MemberHeader entry = fsIndex.getMetadata(file); + if (entry != null) { + result.add(NAME_ATTR, entry.name); + result.add(SIZE_ATTR, entry.size); + } + return result; + } +} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystemFactory.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystemFactory.java new file mode 100644 index 0000000000..822bf7161d --- /dev/null +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystemFactory.java @@ -0,0 +1,73 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.file.formats.omf51; + +import java.io.IOException; + +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.format.omf.AbstractOmfRecordFactory; +import ghidra.app.util.bin.format.omf.OmfException; +import ghidra.app.util.bin.format.omf.OmfRecord; +import ghidra.app.util.bin.format.omf.omf51.Omf51LibraryHeaderRecord; +import ghidra.app.util.bin.format.omf.omf51.Omf51RecordFactory; +import ghidra.app.util.opinion.OmfLoader; +import ghidra.formats.gfilesystem.FSRLRoot; +import ghidra.formats.gfilesystem.FileSystemService; +import ghidra.formats.gfilesystem.factory.GFileSystemFactoryByteProvider; +import ghidra.formats.gfilesystem.factory.GFileSystemProbeByteProvider; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +public class Omf51ArchiveFileSystemFactory implements + GFileSystemFactoryByteProvider, GFileSystemProbeByteProvider { + + @Override + public Omf51ArchiveFileSystem create(FSRLRoot targetFSRL, ByteProvider byteProvider, + FileSystemService fsService, TaskMonitor monitor) + throws IOException, CancelledException { + + Omf51ArchiveFileSystem fs = new Omf51ArchiveFileSystem(targetFSRL, byteProvider); + try { + fs.mount(monitor); + } + catch (OmfException e) { + throw new IOException(e); + } + return fs; + } + + @Override + public boolean probe(ByteProvider byteProvider, FileSystemService fsService, + TaskMonitor monitor) throws IOException, CancelledException { + + if (byteProvider.length() < OmfLoader.MIN_BYTE_LENGTH) { + return false; + } + + try { + AbstractOmfRecordFactory factory = new Omf51RecordFactory(byteProvider); + OmfRecord record = factory.readNextRecord(); + return (record != null && record instanceof Omf51LibraryHeaderRecord); + } + catch (OmfException e) { + return false; + } + catch (IOException e) { + return false; + } + } +} From 90a16f9963ea565f47443c4f7bdf4f449e1d20f4 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Wed, 2 Apr 2025 09:21:37 -0400 Subject: [PATCH 2/3] GP-0: Certify --- .../ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java | 4 ++-- .../file/formats/omf51/Omf51ArchiveFileSystemFactory.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java index 84167ddaf1..ae35d7f381 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.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/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystemFactory.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystemFactory.java index 822bf7161d..1259b99a5f 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystemFactory.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystemFactory.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. From 8701d223567d281e0e01d86013372358d2178edf Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Wed, 2 Apr 2025 09:21:37 -0400 Subject: [PATCH 3/3] GP-0: Certify and cleanup --- .../bin/format/omf/omf51/Omf51Library.java | 72 +++++-------------- .../omf51/Omf51LibraryDictionaryRecord.java | 15 ++-- .../omf/omf51/Omf51LibraryHeaderRecord.java | 4 +- .../omf/omf51/Omf51LibraryModuleLocation.java | 5 +- .../Omf51LibraryModuleLocationsRecord.java | 5 +- .../omf51/Omf51LibraryModuleNamesRecord.java | 2 +- .../ghidra/app/util/opinion/Omf51Loader.java | 1 - .../formats/omf51/Omf51ArchiveFileSystem.java | 34 ++++----- .../omf51/Omf51ArchiveFileSystemFactory.java | 14 ++-- 9 files changed, 53 insertions(+), 99 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Library.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Library.java index a55addbc34..c1cf7643a2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Library.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51Library.java @@ -19,92 +19,54 @@ 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.app.util.bin.format.omf.omf.OmfLibraryRecord.MemberHeader; -import ghidra.program.model.data.*; -import ghidra.util.Msg; -import ghidra.util.exception.DuplicateNameException; +import ghidra.app.util.bin.format.omf.OmfException; +import ghidra.app.util.bin.format.omf.OmfString; public class Omf51Library { - private Omf51RecordFactory factory; - private ArrayList members = new ArrayList<>(); + private List members = new ArrayList<>(); + + public record MemberHeader(long offset, long size, String name) {} - public static class MemberHeader { - public long offset; - public long size; - public String name; - } - /** * Creates a new {@link Omf51Library} * - * @param reader A {@link BinaryReader} positioned at the start of the record - */ - public Omf51Library(Omf51RecordFactory factory) { - this.factory = factory; - } - - /** - * Attempts to parse OMF-51 library members - * + * @param factory A {@link Omf51RecordFactory} * @throws IOException if an IO-related error occurred * @throws OmfException if the required OMF-51 records could not be read */ - public void parseMembers() throws IOException, OmfException { - OmfRecord record = factory.readNextRecord(); - - if (record == null || !(record instanceof Omf51LibraryHeaderRecord)) { + public Omf51Library(Omf51RecordFactory factory) throws OmfException, IOException { + if (!(factory.readNextRecord() instanceof Omf51LibraryHeaderRecord libraryHeader)) { throw new OmfException("Unable to read library header record"); } - Omf51LibraryHeaderRecord libraryHeader = (Omf51LibraryHeaderRecord)record; - factory.getReader().setPointerIndex(libraryHeader.getModNamesOffset()); - record = factory.readNextRecord(); - - if (record == null || !(record instanceof Omf51LibraryModuleNamesRecord)) { + if (!(factory.readNextRecord() instanceof Omf51LibraryModuleNamesRecord modNamesRecord)) { throw new OmfException("Unable to read library module names record"); } - Omf51LibraryModuleNamesRecord modNamesRecord = (Omf51LibraryModuleNamesRecord)record; - - record = factory.readNextRecord(); - - if (record == null || !(record instanceof Omf51LibraryModuleLocationsRecord)) { + if (!(factory.readNextRecord() instanceof Omf51LibraryModuleLocationsRecord modLocations)) { throw new OmfException("Unable to read library module locations record"); } - Omf51LibraryModuleLocationsRecord modLocations = (Omf51LibraryModuleLocationsRecord)record; List locations = modLocations.getLocations(); int index = 0; - Msg.info(this, "Iterating mod names"); for (OmfString moduleName : modNamesRecord.getNames()) { - int size = 0; - if (index + 1 < locations.size()) { - size = locations.get(index + 1).getOffset() - locations.get(index).getOffset(); - } else { - size = libraryHeader.getModNamesOffset() - locations.get(index).getOffset(); - } - - MemberHeader header = new MemberHeader(); - header.name = moduleName.str(); - header.size = size; - header.offset = locations.get(index).getOffset(); - - members.add(header); - + int currentOffset = locations.get(index).getOffset(); + int nextOffset = index + 1 < locations.size() ? locations.get(index + 1).getOffset() + : libraryHeader.getModNamesOffset(); + int size = nextOffset - currentOffset; + members.add(new MemberHeader(currentOffset, size, moduleName.str())); index++; } } - + /** * {@return the list of members} */ - public ArrayList getMembers() { + public List getMembers() { return members; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryDictionaryRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryDictionaryRecord.java index 32de250a3f..48a2f67cd6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryDictionaryRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryDictionaryRecord.java @@ -16,8 +16,8 @@ package ghidra.app.util.bin.format.omf.omf51; import java.io.IOException; -import java.util.List; import java.util.ArrayList; +import java.util.List; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.format.omf.*; @@ -27,9 +27,9 @@ import ghidra.util.exception.DuplicateNameException; public class Omf51LibraryDictionaryRecord extends OmfRecord { private List> moduleSymbolMap = new ArrayList<>(); - + /** - * Creates a new {@link Omf51LibraryDictionaryRecord} record + * Creates a new {@link Omf51LibraryDictionaryRecord} * * @param reader A {@link BinaryReader} positioned at the start of the record * @throws IOException if an IO-related error occurred @@ -48,10 +48,10 @@ public class Omf51LibraryDictionaryRecord extends OmfRecord { dataReader.readNextByte(); moduleSymbolMap.add(symbols); symbols = new ArrayList<>(); - continue; } - - symbols.add(OmfUtils.readString(dataReader)); + else { + symbols.add(OmfUtils.readString(dataReader)); + } } } @@ -63,7 +63,8 @@ public class Omf51LibraryDictionaryRecord extends OmfRecord { Integer moduleIndex = 0; for (List symbols : moduleSymbolMap) { for (OmfString symbol : symbols) { - struct.add(symbol.toDataType(), symbol.getDataTypeSize(), "symbol%d".formatted(moduleIndex), null); + struct.add(symbol.toDataType(), symbol.getDataTypeSize(), + "symbol%d".formatted(moduleIndex), null); } struct.add(BYTE, "terminator%d".formatted(moduleIndex), null); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryHeaderRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryHeaderRecord.java index 6cbe0f22ae..cef6fc758a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryHeaderRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryHeaderRecord.java @@ -16,11 +16,9 @@ package ghidra.app.util.bin.format.omf.omf51; import java.io.IOException; -import java.util.ArrayList; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.format.omf.*; -import ghidra.app.util.bin.format.omf.omf.OmfLibraryRecord.MemberHeader; import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; @@ -31,7 +29,7 @@ public class Omf51LibraryHeaderRecord extends OmfRecord { private short moduleCount; private short blockNumber; private short byteNumber; - + /** * Creates a new {@link Omf51LibraryHeaderRecord} record * diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocation.java index c4a47f6d47..13e1a81fcf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocation.java @@ -19,9 +19,8 @@ import java.io.IOException; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.StructConverter; -import ghidra.app.util.bin.format.omf.*; +import ghidra.app.util.bin.format.omf.OmfUtils; import ghidra.program.model.data.*; -import ghidra.util.exception.DuplicateNameException; public class Omf51LibraryModuleLocation { @@ -62,7 +61,7 @@ public class Omf51LibraryModuleLocation { return (blockNumber * BLOCK_SIZE) + byteNumber; } - public static DataType toDataType() throws DuplicateNameException, IOException { + public static DataType toDataType() { StructureDataType struct = new StructureDataType("Omf51LibraryModuleLocation", 0); struct.add(StructConverter.WORD, "blockNumber", null); struct.add(StructConverter.WORD, "byteNumber", null); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocationsRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocationsRecord.java index 6542d44e08..9632f4eafd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocationsRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleLocationsRecord.java @@ -27,7 +27,7 @@ import ghidra.util.exception.DuplicateNameException; public class Omf51LibraryModuleLocationsRecord extends OmfRecord { private List locations = new ArrayList<>(); - + /** * Creates a new {@link Omf51LibraryModuleLocationsRecord} record * @@ -50,8 +50,7 @@ public class Omf51LibraryModuleLocationsRecord extends OmfRecord { StructureDataType struct = new StructureDataType(Omf51RecordTypes.getName(recordType), 0); struct.add(BYTE, "type", null); struct.add(WORD, "length", null); - - struct.add(new ArrayDataType(Omf51LibraryModuleLocation.toDataType(), locations.size()), + struct.add(new ArrayDataType(Omf51LibraryModuleLocation.toDataType(), locations.size()), "locations", null); struct.add(BYTE, "checksum", null); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleNamesRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleNamesRecord.java index 71f26d314c..f42e2d3492 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleNamesRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/omf/omf51/Omf51LibraryModuleNamesRecord.java @@ -27,7 +27,7 @@ import ghidra.util.exception.DuplicateNameException; public class Omf51LibraryModuleNamesRecord extends OmfRecord { private List names = new ArrayList<>(); - + /** * Creates a new {@link Omf51LibraryModuleNamesRecord} record * 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 e33732b2ff..66edcdb1d2 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 @@ -338,7 +338,6 @@ public class Omf51Loader extends AbstractProgramWrapperLoader { if (record instanceof Omf51PublicDefsRecord publicDefRec) { for (Omf51PublicDef def : publicDefRec.getDefinitions()) { if (def.getUsageType() == Omf51PublicDef.NUMBER) { - log.appendMsg("Skipping NUMBER public def"); continue; } Address segmentAddr = segmentToAddr.get(def.getSegId()); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java index ae35d7f381..09d1ca92b4 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf51/Omf51ArchiveFileSystem.java @@ -18,19 +18,24 @@ package ghidra.file.formats.omf51; import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*; import java.io.IOException; -import java.util.ArrayList; +import java.util.List; import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.ByteProviderWrapper; import ghidra.app.util.bin.format.omf.OmfException; -import ghidra.app.util.bin.format.omf.omf51.*; +import ghidra.app.util.bin.format.omf.omf51.Omf51Library; +import ghidra.app.util.bin.format.omf.omf51.Omf51RecordFactory; import ghidra.formats.gfilesystem.*; import ghidra.formats.gfilesystem.annotations.FileSystemInfo; import ghidra.formats.gfilesystem.fileinfo.FileAttributes; -import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -@FileSystemInfo(type = "omf51", description = "OMF51 Library", factory = Omf51ArchiveFileSystemFactory.class) +@FileSystemInfo( + type = "omf51", + description = "OMF51 Library", + factory = Omf51ArchiveFileSystemFactory.class +) public class Omf51ArchiveFileSystem extends AbstractFileSystem { private ByteProvider provider; @@ -40,18 +45,15 @@ public class Omf51ArchiveFileSystem extends AbstractFileSystem members = library.getMembers(); - - Msg.debug(this, "Found %d members".formatted(members.size())); + List members = new Omf51Library(factory).getMembers(); + monitor.initialize(members.size(), "Opening OMF51 library..."); for (Omf51Library.MemberHeader member : members) { - Msg.debug(this, member.name); - fsIndex.storeFile(member.name, fsIndex.getFileCount(), false, member.size, member); + monitor.increment(); + fsIndex.storeFile(member.name(), fsIndex.getFileCount(), false, member.size(), member); } } @@ -74,7 +76,7 @@ public class Omf51ArchiveFileSystem extends AbstractFileSystem