Merge remote-tracking branch 'origin/GP-5664_ghizard_PdbReader_parse_NewFramePointerOmission_data'

This commit is contained in:
Ryan Kurtz
2025-05-14 15:43:31 -04:00
3 changed files with 241 additions and 9 deletions
@@ -47,7 +47,8 @@ public class DebugData {
X_DATA(7), X_DATA(7),
P_DATA(8), P_DATA(8),
NEW_FRAME_POINTER_OMISSION(9), NEW_FRAME_POINTER_OMISSION(9),
SECTION_HEADER_ORIG(10); SECTION_HEADER_ORIG(10),
UNKNOWN_DEBUG_11(11); // should be renamed when identified
private final int value; private final int value;
@@ -180,6 +181,20 @@ public class DebugData {
return deserializeSectionHeaders(streamNum); return deserializeSectionHeaders(streamNum);
} }
/**
* Returns the New Frame Pointer Omission data (FrameDataRecord)
* @return the frame data or null if does not exist or problem parsing
* @throws CancelledException upon user cancellation
*/
public List<FrameDataRecord> getNewFramePointerOmissionData()
throws CancelledException {
int streamNum = getDebugStream(DebugType.NEW_FRAME_POINTER_OMISSION);
if (streamNum == MsfStream.NIL_STREAM_NUMBER) {
return null;
}
return deserializeNewFramePointerOmissionData(streamNum);
}
/** /**
* Deserialize {@link DebugData} header from the {@link PdbByteReader} input. This parses * Deserialize {@link DebugData} header from the {@link PdbByteReader} input. This parses
* stream numbers for varying Debug Types--the order/location of the stream number is for * stream numbers for varying Debug Types--the order/location of the stream number is for
@@ -259,6 +274,9 @@ public class DebugData {
case SECTION_HEADER_ORIG: case SECTION_HEADER_ORIG:
// imageSectionHeadersOrig = deserializeSectionHeaders(streamNum); // imageSectionHeadersOrig = deserializeSectionHeaders(streamNum);
break; break;
case UNKNOWN_DEBUG_11:
// unknown
break;
} }
} }
} }
@@ -426,6 +444,28 @@ public class DebugData {
} }
private List<FrameDataRecord> deserializeNewFramePointerOmissionData(int streamNum)
throws CancelledException {
// TODO: check implementation for completeness.
try {
PdbByteReader reader = pdb.getReaderForStreamNumber(streamNum);
List<FrameDataRecord> frameData = new ArrayList<>();
while (reader.hasMore()) {
pdb.checkCancelled();
FrameDataRecord frameDataRecord = new FrameDataRecord();
frameDataRecord.parse(reader);
frameData.add(frameDataRecord);
}
return frameData;
}
catch (PdbException | IOException e) {
//catch (IOException e) {
PdbLog.message("Returning null Debug New Frame Pointer Omission Data due to" +
" problem during deserialization from stream" + streamNum + ": " + e.getMessage());
return null;
}
}
/** /**
* Dumps the {@link DebugData}. This package-protected method is for debugging only * Dumps the {@link DebugData}. This package-protected method is for debugging only
* @param writer {@link Writer} to which to write the debug dump * @param writer {@link Writer} to which to write the debug dump
@@ -504,6 +544,16 @@ public class DebugData {
} }
writer.write("End PData---------------------------------------------------\n"); writer.write("End PData---------------------------------------------------\n");
writer.write("NewFramePointerOmissionData---------------------------------\n");
List<FrameDataRecord> frameData = getNewFramePointerOmissionData();
if (frameData != null) {
for (FrameDataRecord frameDataRecord : frameData) {
pdb.checkCancelled();
frameDataRecord.dump(writer);
}
}
writer.write("End NewFramePointerOmissionData-----------------------------\n");
writer.write("End DebugData-----------------------------------------------\n"); writer.write("End DebugData-----------------------------------------------\n");
} }
@@ -0,0 +1,183 @@
/* ###
* 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;
import java.io.IOException;
import java.io.Writer;
/**
* FRAMEDATA from cvinfo.h
* Most members are coded as unsigned long or unsigned long bit-fields; two are coded as
* unsigned short: prolog and saved regs.
*/
public class FrameDataRecord {
private long rvaStart; // unsigned long
private long numBlockBytes; // unsigned long
private long numLocalBytes; // unsigned long
private long numParamBytes; // unsigned long
private long maxStackBytes; // unsigned long
private long frameFunc; // ? // unsigned long
private int numPrologBytes; // unsigned short
private int numSavedRegBytes; // unsigned short
private boolean hasSEH; // unsigned long bit-field
private boolean hasEH; // unsigned long bit-field
private boolean isFunctionStart; // unsigned long bit-field
private long reserved; // contains shifted/masked remainder of unsigned long
/**
* Returns the RVA start
* @return the RVA start
*/
public long getRvaStart() {
return rvaStart;
}
/**
* Returns the number of bytes in the block (function)
* @return the number of bytes in the block
*/
public long getNumberBlockBytes() {
return numBlockBytes;
}
/**
* Returns the number of bytes used by local variables
* @return the number of bytes used by locals
*/
public long getNumberLocalBytes() {
return numLocalBytes;
}
/**
* Returns the number of bytes used by the parameters
* @return the number of bytes used by parameters
*/
public long getNumberParameterBytes() {
return numParamBytes;
}
/**
* Returns max number of stack bytes
* @return max stack bytes
*/
public long getMaxStackBytes() {
return maxStackBytes;
}
// TODO: change javadoc and method name and underlying variable once we've determined what
// this is
/**
* Returns the frame func... not yet sure what this is
* @return the frame func
*/
public long getFrameFunc() {
return frameFunc;
}
/**
* Returns the number of bytes in the function prolog
* @return the number of bytes in the prolog
*/
public int getNumberFunctionPrologBytes() {
return numPrologBytes;
}
/**
* Returns the number of bytes for saved registers
* @return the number of bytes used by saved registers
*/
public int getNumberSavedRegisterBytes() {
return numSavedRegBytes;
}
/**
* Returns whether has SEH
* @return {@code true} if has SEH
*/
public boolean hasSEH() {
return hasSEH;
}
/**
* Returns whether has EH
* @return {@code true} if has EH
*/
public boolean hasEH() {
return hasEH;
}
/**
* Returns whether is function start
* @return {@code true} if us function start
*/
public boolean isFunctionStart() {
return isFunctionStart;
}
/**
* Returns the value of the reserved, remaining 29 bit-field bits
* @return the value of the reserved field
*/
public long reserved() {
return reserved;
}
public void parse(PdbByteReader reader) throws PdbException {
if (reader.numRemaining() < 32) {
throw new PdbException("Not enough data for FrameDataRecord");
}
rvaStart = reader.parseUnsignedIntVal();
numBlockBytes = reader.parseUnsignedIntVal();
numLocalBytes = reader.parseUnsignedIntVal();
numParamBytes = reader.parseUnsignedIntVal();
maxStackBytes = reader.parseUnsignedIntVal();
frameFunc = reader.parseUnsignedIntVal();
numPrologBytes = reader.parseUnsignedShortVal();
numSavedRegBytes = reader.parseUnsignedShortVal();
reserved = reader.parseUnsignedIntVal();
hasSEH = (reserved & 0x01) == 0x01;
reserved >>= 1;
hasEH = (reserved & 0x01) == 0x01;
reserved >>= 1;
isFunctionStart = (reserved & 0x01) == 0x01;
reserved >>= 1;
reserved &= 0x01ffffff;
}
/**
* Dumps the {@link FramePointerOmissionRecord}. This package-protected method is for
* debugging only.
* @param writer {@link Writer} to which to write the debug dump.
* @throws IOException On issue writing to the {@link Writer}.
*/
void dump(Writer writer) throws IOException {
writer.write("FrameDataRecord---------------------------------------------\n");
writer.write(String.format("rvaStart: 0X%08X\n", rvaStart));
writer.write(String.format("numBlockBytes: 0X%08X\n", numBlockBytes));
writer.write(String.format("numLocalBytes: 0X%08X\n", numLocalBytes));
writer.write(String.format("numParamBytes: 0X%08X\n", numParamBytes));
writer.write(String.format("maxStackBytes: 0X%08X\n", maxStackBytes));
writer.write(String.format("frameFunc: 0X%08X\n", frameFunc));
writer.write(String.format("numPrologBytes: 0X%04X\n", numPrologBytes));
writer.write(String.format("numSavedRegBytes: 0X%04X\n", numSavedRegBytes));
writer.write(
String.format("hasStructuedExceptionHandling: %s\n", Boolean.toString(hasSEH)));
writer.write(String.format("hasExceptionHandling: %s\n", Boolean.toString(hasEH)));
writer.write(String.format("isFunctionStart: %s\n", Boolean.toString(isFunctionStart)));
writer.write("End FrameDataRecord-----------------------------------------\n");
}
}
@@ -198,8 +198,7 @@ public class FramePointerOmissionRecord {
void dump(Writer writer) throws IOException { void dump(Writer writer) throws IOException {
writer.write("FramePointerOmissionRecord----------------------------------\n"); writer.write("FramePointerOmissionRecord----------------------------------\n");
writer.write(String.format("firstFunctionByteOffset: 0X%08X\n", firstFunctionByteOffset)); writer.write(String.format("firstFunctionByteOffset: 0X%08X\n", firstFunctionByteOffset));
writer.write(String.format("firstFunctionByteOffset: 0X%08X\n", firstFunctionByteOffset)); writer.write(String.format("numFunctionBytes: 0X%08X\n", numFunctionBytes));
writer.write(String.format("numFunctionBytes: 0X%08XX\n", numFunctionBytes));
writer.write(String.format("numLocalVariables: 0X%08X\n", numLocalVariables)); writer.write(String.format("numLocalVariables: 0X%08X\n", numLocalVariables));
writer.write(String.format("sizeOfParametersInDwords: 0X%08X\n", sizeOfParametersInDwords)); writer.write(String.format("sizeOfParametersInDwords: 0X%08X\n", sizeOfParametersInDwords));
writer.write(String.format("numFunctionPrologBytes: 0X%04X\n", numFunctionPrologBytes)); writer.write(String.format("numFunctionPrologBytes: 0X%04X\n", numFunctionPrologBytes));