diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java index 2fc92d1c4b..faeed3eb9b 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java @@ -20,8 +20,7 @@ import java.io.Writer; import java.util.ArrayList; import java.util.List; -import ghidra.app.util.bin.format.pdb2.pdbreader.msf.Msf; -import ghidra.app.util.bin.format.pdb2.pdbreader.msf.MsfStream; +import ghidra.app.util.bin.format.pdb2.pdbreader.msf.*; import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol; import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType; import ghidra.app.util.datatype.microsoft.GUID; @@ -469,6 +468,35 @@ public abstract class AbstractPdb implements AutoCloseable { return msf.getMonitor(); } + /** + * Developer mechanism to locate stream and offset within that stream that is associated with + * the given absolute file offset + * @param fileOffset the absolute file offset that we are trying to locate + * @return the stream and offset or {@code null} if not located + */ + public StreamAndOffset getStreamOffsetForAbsoluteFileOffset(long fileOffset) { + if (msf instanceof AbstractMsf myMsf) { + return myMsf.getStreamOffsetForAbsoluteFileOffset(fileOffset); + } + return null; + } + + /** + * Developer mechanism to get a {@code PdbByteReader} for the particular stream, offset, and + * length. + * @param streamNumber the stream number + * @param offset the offset in the stream + * @param length the number of bytes to include from the stream + * @return the reader + * @throws CancelledException upon user cancellation + * @throws IOException upon issue getting the stream for the PDB + */ + public PdbByteReader getDeveloperBytes(int streamNumber, int offset, int length) + throws CancelledException, IOException { + PdbByteReader reader = getReaderForStreamNumber(streamNumber, offset, length); + return reader; + } + /** * Check to see if this monitor has been canceled * @throws CancelledException if monitor has been cancelled diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/AbstractMsf.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/AbstractMsf.java index ceb16291ca..1c0eecc0f9 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/AbstractMsf.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/AbstractMsf.java @@ -238,6 +238,26 @@ public abstract class AbstractMsf implements Msf { } } + /** + * Developer mechanism to locate stream and offset within the stream that contains the + * absolute file offset + * @param fileOffset the absolute file offset that we are trying to locate + * @return the offset in the stream of the file offset or {@code null} if not in the stream + */ + public StreamAndOffset getStreamOffsetForAbsoluteFileOffset(long fileOffset) { + if (fileOffset < 0L || fileOffset > (long) numPages * pageSize) { + return null; + } + for (int number = 0; number < streamTable.getNumStreams(); number++) { + MsfStream s = getStream(number); + Integer offset = s.getStreamOffsetForAbsoluteFileOffset(fileOffset); + if (offset != null) { + return new StreamAndOffset(number, offset); + } + } + return null; + } + //============================================================================================== // Class Internals //============================================================================================== diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfStream.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfStream.java index b4e67b2455..f99f301f72 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfStream.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfStream.java @@ -72,8 +72,7 @@ public class MsfStream { * inability to read required bytes * @throws CancelledException upon user cancellation */ - public byte[] read(int streamOffset, int numToRead) - throws IOException, CancelledException { + public byte[] read(int streamOffset, int numToRead) throws IOException, CancelledException { if (numToRead <= 0) { return null; } @@ -231,12 +230,10 @@ public class MsfStream { * @throws PdbException upon not enough data left to parse * @throws CancelledException upon user cancellation */ - void deserializePageNumbers(PdbByteReader reader) - throws PdbException, CancelledException { + void deserializePageNumbers(PdbByteReader reader) throws PdbException, CancelledException { // This calculations works fine for streamLength = 0 // and even streamLength = -1 (0xffffffff). - int numPages = - Msf.floorDivisionWithLog2Divisor(streamLength, msf.getLog2PageSize()); + int numPages = Msf.floorDivisionWithLog2Divisor(streamLength, msf.getLog2PageSize()); if (msf.getPageNumberSize() == 2) { for (int i = 0; i < numPages; i++) { msf.checkCancelled(); @@ -276,4 +273,27 @@ public class MsfStream { deserializePageNumbers(reader); } + /** + * Developer mechanism to see if the stream hold the absolute file offset and what the + * corresponding stream offset is + * @param fileOffset the absolute file offset that we are trying to locate + * @return the offset in the stream of the file offset or {@code null} if not in the stream + */ + Integer getStreamOffsetForAbsoluteFileOffset(long fileOffset) { + long pageSize = msf.getPageSize(); + for (int pageCount = 0; pageCount < pageList.size(); pageCount++) { + int page = pageList.get(pageCount); + long pageStart = page * pageSize; + long pageEndExclusive = pageStart + pageSize; + if (pageStart <= fileOffset && fileOffset < pageEndExclusive) { + // We must get the offset within page, but then must use the pageCount to determine + // the rest of the streamOffset + Long pageOffset = fileOffset - pageStart; + Long streamOffset = pageCount * pageSize + pageOffset; + return streamOffset.intValue(); + } + } + return null; + } + } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/StreamAndOffset.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/StreamAndOffset.java new file mode 100644 index 0000000000..30000d5992 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/StreamAndOffset.java @@ -0,0 +1,18 @@ +/* ### + * 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.pdb2.pdbreader.msf; + +public record StreamAndOffset(int number, int offset) {}