GP-5072 - PDB - Progress on fixing up the processing of older PDB

versions
This commit is contained in:
ghizard 2024-10-29 14:07:54 -04:00
parent 7ddd8665b7
commit fb15252442
8 changed files with 88 additions and 51 deletions

View File

@ -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,7 +19,6 @@ import java.io.IOException;
import java.io.Writer;
import java.util.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.msf.MsfStream;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
@ -246,11 +245,9 @@ public abstract class AbstractSymbolInformation {
}
protected void deserializeHashHeader() throws PdbException, CancelledException, IOException {
MsfStream stream = pdb.getMsf().getStream(streamNumber);
PdbByteReader reader =
pdb.getReaderForStreamNumber(streamNumber, symbolHashOffset,
HASH_HEADER_MIN_READ_LENGTH);
deserializeHashHeader(reader, stream.getLength());
PdbByteReader reader = pdb.getReaderForStreamNumber(streamNumber, symbolHashOffset,
HASH_HEADER_MIN_READ_LENGTH);
deserializeHashHeader(reader);
}
/**
@ -258,7 +255,7 @@ public abstract class AbstractSymbolInformation {
* @param reader {@link PdbByteReader} containing the data buffer to process
* @throws PdbException upon not enough data left to parse
*/
private void deserializeHashHeader(PdbByteReader reader, int streamLength) throws PdbException {
private void deserializeHashHeader(PdbByteReader reader) throws PdbException {
headerSignature = reader.parseInt();
if (headerSignature == HEADER_SIGNATURE) {
hashHeaderLength = HASH_70_HEADER_LENGTH;
@ -273,10 +270,10 @@ public abstract class AbstractSymbolInformation {
reader.reset(); // There was no header
// Calculate the values
bucketsLength = 4 * (numHashRecords + 1);
if (streamLength < bucketsLength) {
if (symbolHashLength < bucketsLength) {
throw new PdbException("Not enough data for symbol hash buckets.");
}
hashRecordsLength = streamLength - bucketsLength;
hashRecordsLength = symbolHashLength - bucketsLength;
hashRecordsOffset = symbolHashOffset + 0;
bucketsOffset = hashRecordsOffset + hashRecordsLength;
}

View File

@ -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.
@ -33,7 +33,6 @@ public class ItemProgramInterfaceParser extends TypeProgramInterfaceParser {
@Override
protected int getStreamNumber() {
return ITEM_PROGRAM_INTERFACE_STREAM_NUMBER;
}
/**
@ -44,7 +43,24 @@ public class ItemProgramInterfaceParser extends TypeProgramInterfaceParser {
@Override
protected RecordCategory getCategory() {
return RecordCategory.ITEM;
}
/**
* Returns whether there is a reasonable error when searching for a version number. For
* the standard TPI case, any unrecognized version number is a reasonable error. For this
* IPI case, we have to be able to error on an unrecognized version number, so we report
* true for version numbers that would seem reasonable. We return false if the versionNumber
* is not a reasonable number, which allows the "hack" to work for IPI where a failure to
* parse an IPI is an indication that there is no IPI (which is first screened by the
* {@link #hackCheckNoNameForStream(NameTable)} method
* @param versionNumber the versionNumber being checked
* @return {@code true} if the error is a reasonable error
*/
@Override
protected boolean isReasonableError(int versionNumber) {
String str = String.format("%d", versionNumber);
return (str.length() == 8 &&
(str.startsWith("199") || str.startsWith("200") || str.startsWith("201")));
}
/**

View File

@ -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.
@ -344,6 +344,7 @@ public abstract class PdbDebugInfo {
// DebugInfo and if the above part (test for SVC600 and SVC1400 would
// be the override method for PdbNewDebugInfo.
else {
substreamReader.reset(); // version number was not a real field
while (substreamReader.hasMore()) {
pdb.checkCancelled();
SectionContribution sectionContribution = new SectionContribution400();

View File

@ -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.
@ -27,7 +27,7 @@ import ghidra.util.exception.CancelledException;
*/
public class PdbOldDebugInfo extends PdbDebugInfo {
private static final int OLD_DBI_HEADER_LENGTH = 22;
private static final int OLD_DBI_HEADER_LENGTH = 24;
//==============================================================================================
// API
@ -49,6 +49,7 @@ public class PdbOldDebugInfo extends PdbDebugInfo {
streamNumberGlobalStaticSymbolsHashMaybe = reader.parseUnsignedShortVal();
streamNumberPublicStaticSymbolsHashMaybe = reader.parseUnsignedShortVal();
streamNumberSymbolRecords = reader.parseUnsignedShortVal();
reader.skip(2); // padding between previous unsigned short and next-to-read int
lengthModuleInformationSubstream = reader.parseInt();
lengthSectionContributionSubstream = reader.parseInt();
lengthSectionMap = reader.parseInt();

View File

@ -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.
@ -29,11 +29,12 @@ public class SectionContribution400 extends SectionContribution {
@Override
void deserialize(PdbByteReader reader) throws PdbException {
isect = reader.parseUnsignedShortVal();
reader.parseBytes(2); // I think there is padding here.
reader.skip(2); // Padding
offset = reader.parseInt();
length = reader.parseInt();
characteristics = reader.parseUnsignedIntVal();
imod = reader.parseUnsignedShortVal();
reader.skip(2); // Padding
}
@Override

View File

@ -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.
@ -189,17 +189,21 @@ public class SymbolRecords {
catch (IOException e) {
throw new PdbException("PDB Error: Not enough data to read CvSigLength");
}
if (getSig) {
cvSignature = reader.parseInt();
}
// 20241029: Neutering this for now; msft-intended logic still not quite understood
// if (getSig) {
// cvSignature = reader.parseInt();
// }
cvSignature = reader.parseInt(); // 20241029: in place of neutered code
int size = 0;
switch (cvSignature) {
case 1:
case 2:
if (streamNumber == cvSignatureCase1and2Stream) {
size = 4;
}
// else size remains 0
// 20241029: Neutering this for now; msft-intended logic still not quite understood
// if (streamNumber == cvSignatureCase1and2Stream) {
// size = 4;
// }
// // else size remains 0
size = 4; // 20241029: in place of neutered code
break;
case 4:
size = 4;
@ -295,7 +299,15 @@ public class SymbolRecords {
try {
PdbByteReader reader;
reader = pdb.getReaderForStreamNumber(streamNumber, offset, 2);
int recordLength = reader.parseUnsignedShortVal();
int recordLength;
try {
recordLength = reader.parseUnsignedShortVal();
}
catch (PdbException pe) {
// Catching this PdbException due to not more data, but letting one from parse()
// (below) get passed to caller
return null;
}
// offset + 2 where 2 is sizeof(short)
PdbByteReader recordReader =
pdb.getReaderForStreamNumber(streamNumber, offset + 2, recordLength);

View File

@ -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.
@ -90,7 +90,19 @@ public class TypeProgramInterfaceParser {
new TypeProgramInterface800(pdb, getCategory(), streamNumber);
break;
default:
throw new PdbException("Unknown TPI Version: " + versionNumber);
// See MSFT "doc" pdb.cpp OpenIpi() note regarding hack.
// For IPI, if normal stream number exists and it is unnamed, then try to
// parse it, and accept it if no errors. However, at this point, this would
// be considered an error here. But we want to be careful about killing
// PDB parsing here for this case. So we have added the isReasonableError()
// check here, which always returns true for TPI, but does special checking
// for IPI. If IPI says it is not a reasonable error (version number looks like
// a non-version number and it is an API), then we must presume that the stream
// is not really an IPI stream and just return null.
if (isReasonableError(versionNumber)) {
throw new PdbException("Unknown TPI Version: " + versionNumber);
}
typeProgramInterface = null;
}
return typeProgramInterface;
@ -117,4 +129,16 @@ public class TypeProgramInterfaceParser {
}
/**
* Returns whether there is a reasonable error when searching for a version number. For
* the standard TPI case, any unrecognized version number is a reasonable error. This
* method gets overridden in IPI for the hack situation described earlier in the default
* case above
* @param versionNumber the versionNumber being checked
* @return {@code true} if the error is a reasonable error
*/
protected boolean isReasonableError(int versionNumber) {
return true;
}
}

View File

@ -1643,7 +1643,6 @@ public class DefaultPdbApplicator implements PdbApplicator {
while (iter.hasNext()) {
monitor.checkCancelled();
procSymNew(iter);
monitor.incrementProgress(1);
}
}
@ -2033,22 +2032,9 @@ public class DefaultPdbApplicator implements PdbApplicator {
return;
}
int totalCount = 0;
int num = debugInfo.getNumModules();
for (int index = 1; index <= num; index++) {
monitor.checkCancelled();
if (index == linkerModuleNumber) {
continue;
}
SymbolGroup symbolGroup = getSymbolGroupForModule(index);
if (symbolGroup == null) {
continue; // should not happen
}
//totalCount += symbolGroup.size();
totalCount++;
}
monitor.setMessage("PDB: Processing module thunks...");
monitor.initialize(totalCount);
monitor.initialize(num);
// Process symbols list for each module
for (int index = 1; index <= num; index++) {
@ -2070,7 +2056,6 @@ public class DefaultPdbApplicator implements PdbApplicator {
else {
iter.next();
}
//monitor.incrementProgress(1);
}
monitor.incrementProgress(1);
}