GP-4246 - PDB improve types processing - better queuing mechanism, remove placeholder types, delay resolve

This commit is contained in:
ghizard
2024-02-27 11:17:35 -05:00
parent 58f1f01799
commit f1495cb6a1
38 changed files with 1141 additions and 1541 deletions
@@ -50,7 +50,7 @@ public class FindDataTypeConflictCauseScript extends GhidraScript {
}
List<DataType> selectedDatatypes = dtmService.getSelectedDatatypes();
if (selectedDatatypes.size() != 1 || !(selectedDatatypes.get(0) instanceof Composite)) {
if (selectedDatatypes.size() != 1) {
popup("Select a single conflict datatype before running script");
return;
}
@@ -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.
@@ -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.
@@ -35,7 +35,6 @@ public class PdbCategories {
private CategoryPath pdbUncategorizedCategory;
private CategoryPath anonymousFunctionsCategory;
private CategoryPath anonymousTypesCategory;
private CategoryPath placeholderTypesCategory;
private CategoryPath baseModuleTypedefsCategory;
private List<CategoryPath> typedefCategories = new ArrayList<>();
@@ -63,8 +62,6 @@ public class PdbCategories {
// anonymousFunctionCount = 0;
anonymousTypesCategory = new CategoryPath(pdbRootCategory, "!_anon_types_");
placeholderTypesCategory = new CategoryPath(pdbRootCategory, "!_placeholder_types_");
}
/**
@@ -193,14 +190,6 @@ public class PdbCategories {
return anonymousTypesCategory;
}
/**
* Returns the {@link CategoryPath} for Anonymous Types Category for the PDB.
* @return the {@link CategoryPath}
*/
public CategoryPath getPlaceholderTypesCategory() {
return placeholderTypesCategory;
}
// /**
// * Returns the name of what should be the next Anonymous Function (based on the count of
// * the number of anonymous functions) so that there is a unique name for the function.
@@ -24,12 +24,12 @@ import ghidra.app.util.pdb.PdbNamespaceUtils;
/**
* Applier for {@link AbstractComplexMsType} types.
*/
public abstract class AbstractComplexTypeApplier extends MsTypeApplier {
public abstract class AbstractComplexTypeApplier extends MsDataTypeApplier {
// Intended for: AbstractComplexMsType
/**
* Constructor for complex type applier.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* Constructor for complex type applier
* @param applicator {@link DefaultPdbApplicator} for which this class is working
*/
public AbstractComplexTypeApplier(DefaultPdbApplicator applicator) {
super(applicator);
@@ -56,12 +56,7 @@ public abstract class AbstractComplexTypeApplier extends MsTypeApplier {
*/
public <T extends AbstractComplexMsType> T getDefinitionType(AbstractComplexMsType mType,
Class<T> type) {
Integer num = applicator.getNumber(mType);
Integer mappedIndex = applicator.getMappedComplexType(num);
if (mappedIndex != null) {
mType =
applicator.getPdb().getTypeRecord(RecordNumber.typeRecordNumber(mappedIndex), type);
}
mType = applicator.getMappedTypeRecord(mType.getRecordNumber(), type);
return type.cast(mType);
}
@@ -75,11 +70,8 @@ public abstract class AbstractComplexTypeApplier extends MsTypeApplier {
//return mine or my def's (and set mine)
SymbolPath getFixedSymbolPath(AbstractComplexMsType type) {
SymbolPath path = getSymbolPath(type);
Integer num = applicator.getNumber(type);
Integer mappedIndex = applicator.getMappedComplexType(num);
if (mappedIndex != null) {
return PdbNamespaceUtils.convertToGhidraPathName(path, mappedIndex);
}
RecordNumber mappedNumber = applicator.getMappedRecordNumber(type.getRecordNumber());
Integer num = mappedNumber.getNumber();
return PdbNamespaceUtils.convertToGhidraPathName(path, num);
}
@@ -30,13 +30,13 @@ import ghidra.util.exception.InvalidInputException;
/**
* Applier for certain function types.
*/
public abstract class AbstractFunctionTypeApplier extends MsTypeApplier {
public abstract class AbstractFunctionTypeApplier extends MsDataTypeApplier {
// Intended for: see children
/**
* Constructor for the applicator that applies a "function" type, transforming it into a
* Ghidra DataType.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* Ghidra DataType
* @param applicator {@link DefaultPdbApplicator} for which this class is working
*/
public AbstractFunctionTypeApplier(DefaultPdbApplicator applicator) {
super(applicator);
@@ -52,28 +52,29 @@ public abstract class AbstractFunctionTypeApplier extends MsTypeApplier {
/**
* Returns the function "this" pointer
* @param type the PDB type being inspected
* @param fixupContext the fixup context to use; or pass in null during fixup process
* @param breakCycle specify {@code true} when employing break-cycle logic (pointers to
* Composites within composites)
* @return the "this" pointer or null if does not have or is not a recognized type
* @throws CancelledException upon user cancellation
* @throws PdbException upon processing error
*/
protected abstract Pointer getThisPointer(AbstractMsType type, FixupContext fixupContext,
boolean breakCycle) throws CancelledException, PdbException;
protected abstract Pointer getThisPointer(AbstractMsType type)
throws CancelledException, PdbException;
/**
* Returns the containing class if function member of class
* Returns the RecordNumber of the function "this" pointer; {@code null} if not this pointer
* @param type the PDB type being inspected
* @param fixupContext the fixup context to use; or pass in null during fixup process
* @param breakCycle specify {@code true} when employing break-cycle logic (pointers to
* Composites within composites)
* @return the containing class composite type
* @throws CancelledException upon user cancellation
* @throws PdbException upon processing error
* @return the record number of the "this" pointer or null if does not have or is not a
* recognized type
*/
protected abstract Composite getContainingComplexApplier(AbstractMsType type,
FixupContext fixupContext, boolean breakCycle) throws CancelledException, PdbException;
protected abstract RecordNumber getThisPointerRecordNumber(AbstractMsType type);
/**
* Returns the RecordNumber of the containing class if function member of class; {@code null}
* if no containing class
* @param type the PDB type being inspected
* @return the record number of the containing class composite type or {@code null} if none
*/
protected abstract RecordNumber getContainingComplexRecordNumber(AbstractMsType type);
/**
* Processes containing class if one exists
@@ -105,23 +106,15 @@ public abstract class AbstractFunctionTypeApplier extends MsTypeApplier {
protected abstract RecordNumber getArgListRecordNumber(AbstractMsType type);
private boolean setReturnType(FunctionDefinitionDataType functionDefinition,
AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws CancelledException, PdbException {
AbstractMsType type) {
if (isConstructor(type)) {
return true;
}
RecordNumber returnRecord = getReturnRecordNumber(type);
if (returnRecord == null) {
return false;
}
DataType returnType =
applicator.getProcessedDataType(returnRecord, fixupContext, breakCycle);
DataType returnType = applicator.getDataType(returnRecord);
if (returnType == null) {
return false;
}
if (applicator.isPlaceholderPointer(returnType)) {
return false;
}
functionDefinition.setReturnType(returnType);
return true;
}
@@ -160,43 +153,30 @@ public abstract class AbstractFunctionTypeApplier extends MsTypeApplier {
}
}
private boolean setArguments(FunctionDefinitionDataType functionDefinition, AbstractMsType type,
FixupContext fixupContext, boolean breakCycle) throws CancelledException, PdbException {
private boolean setArguments(FunctionDefinitionDataType functionDefinition, AbstractMsType type)
throws CancelledException, PdbException {
RecordNumber argsRecord = getArgListRecordNumber(type);
AbstractMsType aType = applicator.getPdb().getTypeRecord(argsRecord);
if (!(aType instanceof AbstractArgumentsListMsType argsList)) {
applicator.appendLogMsg(
"PDB Warning: expecting args list but found " + aType.getClass().getSimpleName() +
" for parameter list of " + functionDefinition.getName());
return false;
}
List<RecordNumber> args = getArgsRecordNumbers(type);
boolean hasPlaceholder = false;
List<RecordNumber> args = argsList.getArgRecordNumbers();
List<ParameterDefinition> parameterDefinitionList = new ArrayList<>();
int parameterCount = 0;
for (RecordNumber arg : args) {
applicator.checkCancelled();
AbstractMsType argMsType = applicator.getPdb().getTypeRecord(arg);
AbstractMsType argMsType = applicator.getTypeRecord(arg);
if (argMsType instanceof PrimitiveMsType primitive && primitive.isNoType()) {
// Arguments list is empty. (There better not have been any arguments up until
// now.)
break;
}
DataType argDataType = applicator.getProcessedDataType(arg, fixupContext, breakCycle);
DataType argDataType = applicator.getDataType(arg);
if (argDataType == null) {
applicator.appendLogMsg(
"PDB Warning: No type conversion for " + argMsType.toString() +
" for parameter " + parameterCount + " of " + functionDefinition.getName());
}
else {
if (applicator.isPlaceholderPointer(argDataType)) {
hasPlaceholder = true;
}
try {
ParameterDefinition parameterDefinition =
new ParameterDefinitionImpl(null, argDataType, "");
@@ -225,49 +205,90 @@ public abstract class AbstractFunctionTypeApplier extends MsTypeApplier {
}
}
}
if (hasPlaceholder) {
return false;
}
functionDefinition.setArguments(parameterDefinitionList
.toArray(new ParameterDefinition[parameterDefinitionList.size()]));
return true;
}
@Override
public DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
boolean apply(AbstractMsType type)
throws CancelledException, PdbException {
DataType existing = applicator.getDataType(type);
if (existing != null) {
return existing;
if (!precheckOrScheduleDependencies(type)) {
return false;
}
FunctionDefinitionDataType functionDefinition = new FunctionDefinitionDataType(
applicator.getAnonymousFunctionsCategory(), "_func", applicator.getDataTypeManager());
boolean hasPlaceholder = false;
processContainingType(type);
setReturnType(functionDefinition, type);
setArguments(functionDefinition, type);
Pointer thisPointer = getThisPointer(type);
if (!setReturnType(functionDefinition, type, fixupContext, breakCycle)) {
hasPlaceholder = true;
}
if (!setArguments(functionDefinition, type, fixupContext, breakCycle)) {
hasPlaceholder = true;
}
if (hasPlaceholder) {
return null;
}
Pointer thisPointer = getThisPointer(type, fixupContext, breakCycle);
CallingConvention convention = getCallingConvention(type);
setCallingConvention(functionDefinition, convention, thisPointer);
DataTypeNamingUtil.setMangledAnonymousFunctionName(functionDefinition);
DataType dataType = functionDefinition;
DataType resolvedType = applicator.resolve(functionDefinition);
applicator.putDataType(type, resolvedType);
return resolvedType;
applicator.putDataType(type, dataType);
return true;
}
/**
* Uses {@link DefaultPdbApplicator#getDataTypeOrSchedule(Integer)}) on all underlying types
* to ensure that the types get scheduled... and detects whether any types were not yet
* available so that this composite type is denoted as not done.
* @param type the MS type of the function
* @return {@code true} if all underlying types are already available
* @throws PdbException upon processing issue
*/
private boolean precheckOrScheduleDependencies(AbstractMsType type)
throws PdbException {
boolean done = true;
RecordNumber returnRecordNumber = getReturnRecordNumber(type);
DataType dt = applicator.getDataTypeOrSchedule(returnRecordNumber);
if (dt == null) {
done = false;
}
List<RecordNumber> args = getArgsRecordNumbers(type);
for (RecordNumber argRecordNumber : args) {
dt = applicator.getDataTypeOrSchedule(argRecordNumber);
if (dt == null) {
done = false;
}
}
RecordNumber thisRecordNumber = getThisPointerRecordNumber(type);
if (thisRecordNumber != null) {
dt = applicator.getDataTypeOrSchedule(thisRecordNumber);
if (dt == null) {
done = false;
}
}
RecordNumber containerRecordNumber = getContainingComplexRecordNumber(type);
if (containerRecordNumber != null) {
dt = applicator.getDataTypeOrSchedule(containerRecordNumber);
if (dt == null) {
done = false;
}
}
return done;
}
private List<RecordNumber> getArgsRecordNumbers(AbstractMsType type) throws PdbException {
RecordNumber argsRecord = getArgListRecordNumber(type);
AbstractMsType aType = applicator.getTypeRecord(argsRecord);
if (!(aType instanceof AbstractArgumentsListMsType argsList)) {
throw new PdbException(
"Expecting arguments list but got: " + aType.getClass().getSimpleName());
}
return argsList.getArgRecordNumbers();
}
}
@@ -15,32 +15,22 @@
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractArgumentsListMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractArgumentsListMsType} types.
*/
public class ArgumentsListTypeApplier extends MsTypeApplier {
public class ArgumentsListTypeApplier extends MsDataTypeComponentApplier {
// Intended for: AbstractArgumentsListMsType
/**
* Constructor for the applicator that applies a arguments list.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* @throws IllegalArgumentException Upon invalid arguments.
* Constructor for the applicator that applies a arguments list
* @param applicator {@link DefaultPdbApplicator} for which this class is working
* @throws IllegalArgumentException Upon invalid arguments
*/
public ArgumentsListTypeApplier(DefaultPdbApplicator applicator) throws IllegalArgumentException {
public ArgumentsListTypeApplier(DefaultPdbApplicator applicator)
throws IllegalArgumentException {
super(applicator);
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
// do nothing
return null;
}
}
@@ -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.
@@ -28,66 +28,50 @@ import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractArrayMsType} types.
*/
public class ArrayTypeApplier extends MsTypeApplier {
public class ArrayTypeApplier extends MsDataTypeApplier {
// Intended for: AbstractArrayMsType
/**
* Constructor for the applicator that applies a "array" type, transforming it into a
* Ghidra DataType.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* Ghidra DataType
* @param applicator {@link DefaultPdbApplicator} for which this class is working
*/
public ArrayTypeApplier(DefaultPdbApplicator applicator) {
super(applicator);
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
return applyType((AbstractArrayMsType) type, fixupContext);
boolean apply(AbstractMsType type) throws PdbException, CancelledException {
AbstractArrayMsType arrayType = (AbstractArrayMsType) type;
// Doing pre-check on type first using the getDataTypeOrSchedule method. The logic is
// simpler here for Composites or Functions because we only have one dependency type,
// so we are not doing a separate call to a pre-check method as there is in those appliers.
// If type is not available, return false.
RecordNumber underlyingRecordNumber = arrayType.getElementTypeRecordNumber();
DataType underlyingDataType = applicator.getDataTypeOrSchedule(underlyingRecordNumber);
if (underlyingDataType == null) {
return false;
}
int numElements = calculateNumElements(arrayType, underlyingDataType);
if (numElements == -1) {
// There was a math calculation problem (probably have the wrong underlying type,
// which we still need to figure out; i.e., better composite mapper) so we
// will change the underlying type for now...
underlyingDataType = Undefined1DataType.dataType;
numElements = getSizeInt(arrayType); // array size (but divided by 1) is array size
}
DataType dataType = new ArrayDataType(underlyingDataType, numElements, -1,
applicator.getDataTypeManager());
applicator.putDataType(arrayType, dataType);
return true;
}
boolean isFlexibleArray(AbstractMsType type) {
return BigInteger.ZERO.equals(type.getSize());
}
private DataType applyType(AbstractArrayMsType type, FixupContext fixupContext)
throws CancelledException, PdbException {
if (fixupContext != null) {
DataType existingDt = applicator.getDataType(type);
if (existingDt != null) {
return existingDt;
}
}
RecordNumber underlyingRecord = type.getElementTypeRecordNumber();
DataType underlyingDataType =
applicator.getProcessedDataType(underlyingRecord, fixupContext, false);
DataType dataType;
if (applicator.isPlaceholderType(underlyingDataType)) {
Long longArraySize = getSizeLong(type);
int intArraySize = longArraySize.intValue();
dataType =
applicator.getPlaceholderArray(intArraySize, underlyingDataType.getAlignment());
}
else {
int numElements = calculateNumElements(type, underlyingDataType);
if (numElements == -1) {
// There was a math calculation problem (probably have the wrong underlying type,
// which we still need to figure out; i.e., better composite mapper) so we
// will change the underlying type for now...
underlyingDataType = Undefined1DataType.dataType;
numElements = getSizeInt(type); // array size (but divided by 1) is array size
}
dataType = new ArrayDataType(underlyingDataType, numElements, -1,
applicator.getDataTypeManager());
}
DataType resolvedType = applicator.resolve(dataType);
applicator.putDataType(type, resolvedType);
return resolvedType;
}
private int calculateNumElements(AbstractArrayMsType type, DataType underlyingDataType) {
if (underlyingDataType == null) {
@@ -15,24 +15,21 @@
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractBaseClassMsType}, {@link AbstractVirtualBaseClassMsType}, and
* {@link AbstractIndirectVirtualBaseClassMsType} types.
*/
public class BaseClassTypeApplier extends MsTypeApplier {
public class BaseClassTypeApplier extends MsDataTypeComponentApplier {
// Intended for: AbstractBaseClassMsType, AbstractVirtualBaseClassMsType, or
// AbstractIndirectVirtualBaseClassMsType
/**
* Constructor for base class applier.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* @throws IllegalArgumentException Upon invalid arguments.
* Constructor for base class applier
* @param applicator {@link DefaultPdbApplicator} for which this class is working
* @throws IllegalArgumentException Upon invalid arguments
*/
public BaseClassTypeApplier(DefaultPdbApplicator applicator)
throws IllegalArgumentException {
@@ -54,13 +51,6 @@ public class BaseClassTypeApplier extends MsTypeApplier {
return ((AbstractIndirectVirtualBaseClassMsType) type).getBaseClassRecordNumber();
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
// do nothing
return null;
}
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractBaseClassMsType) &&
@@ -27,36 +27,42 @@ import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractBitfieldMsType} types.
*/
public class BitfieldTypeApplier extends MsTypeApplier {
public class BitfieldTypeApplier extends MsDataTypeApplier {
// Intended for: AbstractBitfieldMsType
/**
* Constructor for bitfield applier.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* Constructor for bitfield applier
* @param applicator {@link DefaultPdbApplicator} for which this class is working
*/
public BitfieldTypeApplier(DefaultPdbApplicator applicator) {
super(applicator);
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
boolean apply(AbstractMsType type) throws PdbException, CancelledException {
AbstractBitfieldMsType mType = (AbstractBitfieldMsType) type;
// Doing pre-check on type first using the getDataTypeOrSchedule method. The logic is
// simpler here for Composites or Functions because we only have one dependency type,
// so we are not doing a separate call to a pre-check method as there is in those appliers.
// If type is not available, return false.
RecordNumber elementRecordNumber = mType.getElementRecordNumber();
DataType baseDataType =
applicator.getProcessedDataType(elementRecordNumber, fixupContext, breakCycle);
DataType baseDataType = applicator.getDataTypeOrSchedule(elementRecordNumber);
if (baseDataType == null) {
return false;
}
DataType bitFieldDataType;
try {
bitFieldDataType = new Pdb2BitField(baseDataType.clone(applicator.getDataTypeManager()),
mType.getBitLength(), mType.getBitPosition());
}
catch (InvalidDataTypeException e) {
applicator.appendLogMsg(
throw new PdbException(
"Problem creating PdbBitField for " + type.getName() + ", error: " + e.toString());
return null;
}
// do not resolve bit-fields!
return bitFieldDataType;
applicator.putDataType(mType, bitFieldDataType);
return true;
}
/**
@@ -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,6 +19,7 @@ import java.util.*;
import ghidra.app.util.SymbolPath;
import ghidra.app.util.SymbolPathParser;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.TypeProgramInterface;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.util.exception.CancelledException;
@@ -26,7 +27,7 @@ import ghidra.util.task.TaskMonitor;
/**
* Maps forward references with corresponding definitions for composites and enums. Map is of
* record number (index) to record number (index)--always of TYPE RecordCategory, as we are not
* RecordNumber to RecordNumber--always of TYPE RecordCategory, as we are not
* expecting Complex type records numbers to be mapped from ITEM RecordCategory lists. We are
* always creating a map of higher number to lower number, as we are assuming that processing
* will be done in an increasing-record-number order.
@@ -40,35 +41,27 @@ import ghidra.util.task.TaskMonitor;
// tried.
public class ComplexTypeMapper {
private Map<Integer, Integer> map;
private Map<RecordNumber, RecordNumber> map;
//==============================================================================================
public ComplexTypeMapper() {
map = new HashMap<>();
}
// /**
// * Returns map to alternate record number or argument record number if no map. Result is
// * record number of alternative record for the complex type. It should be the lower of the
// * two numbers for the set of fwdref and def records, with the fwdref generally, but not
// * always, the lower-numbered record.
// * @param recordNumber the record number for which to do the lookup
// * @return the mapped number
// */
/**
* Returns map to alternate record number or argument record number if no map. Result is
* record number of alternative record for the complex type. Map is of fwdref to definition
* numbers. The fwdref number is generally, but not always, the lower number
* RecordNumber of alternative record for the complex type. Map is of fwdref to definition
* RecordNumbers. The fwdref number is generally, but not always, the lower number
* @param recordNumber the record number for which to do the lookup
* @return the mapped number
* @return the mapped record number or the original record number if no mapped entry
*/
public Integer getMapped(int recordNumber) {
public RecordNumber getMapped(RecordNumber recordNumber) {
return map.getOrDefault(recordNumber, recordNumber);
}
// Storing type (isFwd or isDef) so that if we decide to parse Types on demand, we will not
// have to parse it again to see if it is a fwdref or def.
private record NumFwdRef(int number, boolean isFwd) {}
private record NumFwdRef(RecordNumber number, boolean isFwd) {}
//==============================================================================================
//==============================================================================================
@@ -131,6 +124,7 @@ public class ComplexTypeMapper {
SymbolPath symbolPath = new SymbolPath(SymbolPathParser.parse(complexType.getName()));
boolean isFwdRef = complexType.getMsProperty().isForwardReference();
RecordNumber recordNumber = complexType.getRecordNumber();
Deque<NumFwdRef> numTypeFIFO = typeFIFOsByPath.get(symbolPath);
if (numTypeFIFO == null) {
@@ -138,7 +132,7 @@ public class ComplexTypeMapper {
typeFIFOsByPath.put(symbolPath, numTypeFIFO);
// Putting forward reference or definition (doesn't matter which it is)
if (!numTypeFIFO.add(new NumFwdRef(indexNumber, isFwdRef))) {
if (!numTypeFIFO.add(new NumFwdRef(recordNumber, isFwdRef))) {
// Error
}
}
@@ -148,7 +142,7 @@ public class ComplexTypeMapper {
// If same in FIFO, then add to bottom of the FIFO, as all records on this FIFO
// will be the same per this algorithm.
if (firstNumFwdRef.isFwd() == isFwdRef) {
if (!numTypeFIFO.add(new NumFwdRef(indexNumber, isFwdRef))) {
if (!numTypeFIFO.add(new NumFwdRef(recordNumber, isFwdRef))) {
// Error
}
}
@@ -167,7 +161,7 @@ public class ComplexTypeMapper {
// symbol path and we need the fwdref symbol path to be the same. Thus we
// want to be able to have ready access to the def record.
if (isFwdRef) {
map.put(indexNumber, firstNumFwdRef.number());
map.put(recordNumber, firstNumFwdRef.number());
// // Following is just temporary during development to compare with
// // previous mapping capability. TODO remove
// System.out.println(String.format("%d %s %d -> %d",
@@ -175,7 +169,7 @@ public class ComplexTypeMapper {
// indexNumber, firstNumFwdRef.number()));
}
else {
map.put(firstNumFwdRef.number(), indexNumber);
map.put(firstNumFwdRef.number(), recordNumber);
// // Following is just temporary during development to compare with
// // previous mapping capability. TODO remove
// System.out.println(String.format("%d %s %d <- %d",
@@ -15,7 +15,8 @@
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.MsSymbolIterator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractDataMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.program.model.address.Address;
@@ -47,14 +48,13 @@ public class DataSymbolApplier extends MsSymbolApplier
@Override
public void apply(MsSymbolIterator iter) throws PdbException, CancelledException {
getValidatedSymbol(iter, true);
Address address = applicator.getAddress(symbol);
if (applicator.isInvalidAddress(address, symbol.getName())) {
return;
}
// createData() can return false on failure, but we want to put the symbol down regardless
createData(address);
Address symbolAddress = applicator.getAddress(symbol);
if (applicator.isInvalidAddress(symbolAddress, symbol.getName())) {
return;
}
RecordNumber typeRecordNumber = symbol.getTypeRecordNumber();
if (!createData(symbol, symbolAddress, typeRecordNumber)) {
return;
}
applicator.createSymbol(symbolAddress, symbol.getName(), false);
}
@@ -62,36 +62,29 @@ public class DataSymbolApplier extends MsSymbolApplier
public void applyTo(NestingSymbolApplier applyToApplier, MsSymbolIterator iter)
throws PdbException, CancelledException {
getValidatedSymbol(iter, true);
if (applyToApplier instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applyToApplier;
DataType dataType = applicator.getCompletedDataType(symbol.getTypeRecordNumber());
if (dataType == null) { // TODO: check that we can have null here.
return; // silently return.
}
if (applyToApplier instanceof FunctionSymbolApplier functionSymbolApplier) {
Address address = applicator.getAddress(symbol);
if (!createData(symbol, address, dataType)) {
return;
if (applicator.isInvalidAddress(address, symbol.getName())) {
return; // silently return
}
// createData() can return false on failure, but we want to put the symbol down regardless
createData(address);
DataType dataType = applicator.getCompletedDataType(symbol.getTypeRecordNumber());
functionSymbolApplier.setLocalVariable(address, symbol.getName(), dataType);
}
}
MsTypeApplier getTypeApplier(AbstractMsSymbol abstractSymbol) {
if (!(abstractSymbol instanceof AbstractDataMsSymbol symbol)) {
if (!(abstractSymbol instanceof AbstractDataMsSymbol dataSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
return applicator.getTypeApplier(symbol.getTypeRecordNumber());
return applicator.getTypeApplier(dataSymbol.getTypeRecordNumber());
}
boolean createData(AbstractDataMsSymbol symbol, Address address, RecordNumber typeRecordNumber)
throws CancelledException, PdbException {
DataType dataType = applicator.getCompletedDataType(typeRecordNumber);
return createData(symbol, address, dataType);
}
boolean createData(AbstractDataMsSymbol symbol, Address address, DataType dataType) {
if (applicator.isInvalidAddress(address, symbol.getName())) {
boolean createData(Address address) throws CancelledException, PdbException {
DataType dataType = applicator.getCompletedDataType(symbol.getTypeRecordNumber());
if (dataType == null) { // TODO: check that we can have null here.
return false;
}
if (applicator.getImageBase().equals(address) &&
@@ -121,7 +114,7 @@ public class DataSymbolApplier extends MsSymbolApplier
if (cu != null) {
if ((cu instanceof Instruction) || !address.equals(cu.getAddress())) {
applicator.appendLogMsg("Warning: Did not create data type \"" +
dataType.getDisplayName() + "\" at address " + address + " due to conflict");
dataType.getName() + "\" at address " + address + " due to conflict");
return;
}
Data d = (Data) cu;
@@ -21,7 +21,6 @@ import ghidra.app.util.SymbolPath;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
@@ -32,17 +31,15 @@ public class EnumTypeApplier extends AbstractComplexTypeApplier {
// Intended for: AbstractEnumMsType
/**
* Constructor for enum type applier, for transforming a enum into a
* Ghidra DataType.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* Constructor for enum type applier, for transforming a enum into a Ghidra DataType
* @param applicator {@link DefaultPdbApplicator} for which this class is working
*/
public EnumTypeApplier(DefaultPdbApplicator applicator) {
super(applicator);
}
private long getMask(AbstractEnumMsType type, FixupContext fixupContext, boolean breakCycle)
throws CancelledException, PdbException {
switch (getLength(type, fixupContext, breakCycle)) {
private long getMask(AbstractEnumMsType type) {
switch (getLength(type)) {
case 1:
return 0xffL;
case 2:
@@ -54,24 +51,21 @@ public class EnumTypeApplier extends AbstractComplexTypeApplier {
}
}
private int getLength(AbstractEnumMsType type, FixupContext fixupContext, boolean breakCycle)
throws CancelledException, PdbException {
DataType underlyingDataType = getUnderlyingDataType(type, fixupContext, breakCycle);
private int getLength(AbstractEnumMsType type) {
DataType underlyingDataType = getUnderlyingDataType(type);
if (underlyingDataType == null) {
return 1;
}
return Integer.max(underlyingDataType.getLength(), 1);
}
private DataType getUnderlyingDataType(AbstractEnumMsType type, FixupContext fixupContext,
boolean breakCycle) throws CancelledException, PdbException {
private DataType getUnderlyingDataType(AbstractEnumMsType type) {
RecordNumber underlyingRecordNumber = type.getUnderlyingRecordNumber();
return applicator.getProcessedDataType(underlyingRecordNumber, fixupContext, breakCycle);
return applicator.getDataType(underlyingRecordNumber);
}
boolean isSigned(AbstractEnumMsType type, FixupContext fixupContext, boolean breakCycle)
throws CancelledException, PdbException {
DataType underlyingType = getUnderlyingDataType(type, fixupContext, breakCycle);
boolean isSigned(AbstractEnumMsType type) {
DataType underlyingType = getUnderlyingDataType(type);
if (underlyingType == null) {
return false;
}
@@ -81,8 +75,7 @@ public class EnumTypeApplier extends AbstractComplexTypeApplier {
return false;
}
private EnumDataType createEmptyEnum(AbstractEnumMsType type, FixupContext fixupContext,
boolean breakCycle) throws CancelledException, PdbException {
private EnumDataType createEmptyEnum(AbstractEnumMsType type) {
AbstractEnumMsType defType = getDefinitionType(type);
@@ -90,39 +83,31 @@ public class EnumTypeApplier extends AbstractComplexTypeApplier {
CategoryPath categoryPath = applicator.getCategory(fixedPath.getParent());
EnumDataType enumDataType = new EnumDataType(categoryPath, fixedPath.getName(),
getLength(defType, fixupContext, breakCycle), applicator.getDataTypeManager());
getLength(defType), applicator.getDataTypeManager());
return enumDataType;
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
boolean apply(AbstractMsType type)
throws PdbException, CancelledException {
//Ghidra cannot handle fwdrefs and separate definitions for enumerates as it can for
// composites. Thus, we will just try to provide the defined version now.
Integer number = applicator.getNumber(type);
Integer mapped = applicator.getMappedComplexType(number);
AbstractEnumMsType definedEnum = (AbstractEnumMsType) applicator.getPdb()
.getTypeRecord(RecordNumber.typeRecordNumber(mapped));
AbstractEnumMsType definedEnum =
(AbstractEnumMsType) applicator.getMappedTypeRecord(type.getRecordNumber());
DataType existingDt = applicator.getDataType(mapped);
if (existingDt != null) {
if (!(existingDt instanceof Enum)) {
throw new PdbException("PDB error retrieving Enum type");
}
return existingDt;
}
// Note that we do not need to check on underlying data types, as there are none.
EnumDataType enumDataType = createEmptyEnum(definedEnum, fixupContext, breakCycle);
applyEnumMsType(enumDataType, definedEnum, fixupContext, breakCycle);
EnumDataType enumDataType = createEmptyEnum(definedEnum);
applyEnumMsType(enumDataType, definedEnum);
DataType dataType = applicator.resolve(enumDataType);
applicator.putDataType(mapped, dataType);
return dataType;
DataType dataType = enumDataType;
applicator.putDataType(definedEnum, dataType);
return true;
}
private EnumDataType applyEnumMsType(EnumDataType enumDataType, AbstractEnumMsType type,
FixupContext fixupContext, boolean breakCycle) throws PdbException, CancelledException {
private EnumDataType applyEnumMsType(EnumDataType enumDataType, AbstractEnumMsType type)
throws PdbException {
if (enumDataType.getCount() != 0) {
//already applied
@@ -147,19 +132,18 @@ public class EnumTypeApplier extends AbstractComplexTypeApplier {
enumerates.size() + " available for " + fullPathName);
}
int length = getLength(type, fixupContext, breakCycle);
boolean isSigned = isSigned(type, fixupContext, breakCycle);
int length = getLength(type);
boolean isSigned = isSigned(type);
for (AbstractEnumerateMsType enumerateType : enumerates) {
SymbolPath memberSymbolPath = new SymbolPath(enumerateType.getName());
enumDataType.add(memberSymbolPath.getName(), narrowingConversion(type, length, isSigned,
enumerateType.getNumeric(), fixupContext, breakCycle));
enumerateType.getNumeric()));
}
return enumDataType;
}
private long narrowingConversion(AbstractEnumMsType type, int outputSize, boolean outputSigned,
Numeric numeric, FixupContext fixupContext, boolean breakCycle)
throws CancelledException, PdbException {
Numeric numeric) {
if (!numeric.isIntegral()) {
Msg.info(this, "Non-integral numeric found: " + numeric);
return 0;
@@ -168,7 +152,7 @@ public class EnumTypeApplier extends AbstractComplexTypeApplier {
pdbLogAndInfoMessage(this, "Using zero in place of non-integral enumerate: " + numeric);
return 0L; //
}
return numeric.getIntegral().longValue() & getMask(type, fixupContext, breakCycle);
return numeric.getIntegral().longValue() & getMask(type);
}
private AbstractEnumMsType getDefinitionType(AbstractComplexMsType type) {
@@ -16,37 +16,24 @@
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.Numeric;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractEnumerateMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.app.util.pdb.PdbNamespaceUtils;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractEnumerateMsType} types.
*/
public class EnumerateTypeApplier extends MsTypeApplier {
public class EnumerateTypeApplier extends MsDataTypeComponentApplier {
// Intended for: AbstractEnumerateMsType
/**
* Constructor for enumerate type applier, for transforming a enumerate into a
* Ghidra DataType.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* Constructor for enumerate type applier, for transforming a enumerate into a Ghidra DataType
* @param applicator {@link DefaultPdbApplicator} for which this class is working
*/
public EnumerateTypeApplier(DefaultPdbApplicator applicator) {
super(applicator);
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
DataType dataType = applyEnumerateMsType((AbstractEnumerateMsType) type);
//dataType is null for now... so no resolve
//return applicator.resolve(dataType);
return null;
}
String getName(AbstractEnumerateMsType type) {
return PdbNamespaceUtils.fixUnnamed(type.getName(), type.getRecordNumber().getNumber());
}
@@ -21,14 +21,12 @@ import java.util.List;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractFieldListMsType} types and {@code NO_TYPE} when in place of the
* former type.
*/
public class FieldListTypeApplier extends MsTypeApplier {
public class FieldListTypeApplier extends MsDataTypeComponentApplier {
//TODO: evaluate the static method and multiple constructors... what can be cleaned up with
// regard to these and the possible NoType record???
@@ -46,35 +44,30 @@ public class FieldListTypeApplier extends MsTypeApplier {
}
/**
* Constructor.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* @throws IllegalArgumentException Upon invalid arguments.
* Constructor
* @param applicator {@link DefaultPdbApplicator} for which this class is working
* @throws IllegalArgumentException Upon invalid arguments
*/
public FieldListTypeApplier(DefaultPdbApplicator applicator) throws IllegalArgumentException {
super(applicator);
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
// do nothing
return null;
}
//==============================================================================================
record FieldLists(List<AbstractMsType> bases, List<AbstractMsType> members,
List<AbstractMemberMsType> nonstaticMembers,
List<AbstractStaticMemberMsType> staticMembers,
List<AbstractVirtualFunctionTablePointerMsType> vftPtrs, List<AbstractMsType> methods,
List<AbstractNestedTypeMsType> nestedTypes, List<AbstractEnumerateMsType> enumerates) {}
//==============================================================================================
FieldLists getFieldLists(RecordNumber recordNumber) throws PdbException {
AbstractMsType type = applicator.getPdb().getTypeRecord(recordNumber);
AbstractMsType type = applicator.getTypeRecord(recordNumber);
if (type instanceof PrimitiveMsType primitive && primitive.isNoType()) {
return new FieldLists(new ArrayList<>(), new ArrayList<>(), new ArrayList<>(),
new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(),
new ArrayList<>());
}
else if (type instanceof AbstractFieldListMsType fieldListType) {
return getFieldLists(fieldListType);
@@ -98,6 +91,8 @@ public class FieldListTypeApplier extends MsTypeApplier {
}
List<AbstractMemberMsType> nonstaticMembers =
new ArrayList<>(fieldListType.getNonStaticMembers());
List<AbstractStaticMemberMsType> staticMembers =
new ArrayList<>(fieldListType.getStaticMembers());
List<AbstractVirtualFunctionTablePointerMsType> vftPtrs =
new ArrayList<>(fieldListType.getVftPointers());
List<AbstractNestedTypeMsType> nestedTypes =
@@ -115,6 +110,7 @@ public class FieldListTypeApplier extends MsTypeApplier {
members.addAll(lists.members());
methods.addAll(lists.methods());
nonstaticMembers.addAll(lists.nonstaticMembers());
staticMembers.addAll(lists.staticMembers());
vftPtrs.addAll(lists.vftPtrs());
nestedTypes.addAll(lists.nestedTypes());
enumerates.addAll(lists.enumerates());
@@ -124,7 +120,8 @@ public class FieldListTypeApplier extends MsTypeApplier {
}
}
return new FieldLists(bases, members, nonstaticMembers, vftPtrs, methods, nestedTypes,
return new FieldLists(bases, members, nonstaticMembers, staticMembers, vftPtrs, methods,
nestedTypes,
enumerates);
}
@@ -1,313 +0,0 @@
/* ###
* 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.pdb.pdbapplicator;
import java.util.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.program.model.data.DataType;
/**
* Applier context used for fixups of data types
*/
public class FixupContext {
private Deque<Integer> stagedRecordFifo = new ArrayDeque<>();
private Deque<Integer> inProgressRecordStack = new ArrayDeque<>();
private Deque<Integer> fixupRecordFifo = new ArrayDeque<>();
private Map<Integer, List<Integer>> map = new HashMap<>();
private Map<Integer, DataType> fixupTypes = new HashMap<>();
/**
* Checks that record is already being processed and, if not, adds it to the Staged state
* @param record the number of the record
* @throws PdbException upon the record already in the process or fixup state
*/
void ensureInContext(int record) throws PdbException {
if (inProgressRecordStack.contains(record)) {
return;
}
if (fixupRecordFifo.contains(record)) {
return;
}
addStagedRecord(record);
}
DataType getFixupDataType(int record) {
return fixupTypes.get(record);
}
/**
* Adds record to the Staged state if not already there
* @param recordNumber the number of the record
* @throws PdbException upon the record already in the process or fixup state
*/
void addStagedRecord(int recordNumber) throws PdbException {
if (stagedRecordFifo.contains(recordNumber)) {
return;
}
if (inProgressRecordStack.contains(recordNumber)) {
throw new PdbException("Record Number in process state: " + recordNumber);
}
if (fixupRecordFifo.contains(recordNumber)) {
throw new PdbException("Record Number in fixup state: " + recordNumber);
}
if (map.containsKey(recordNumber)) {
throw new PdbException("Record Number already exists: " + recordNumber);
}
map.put(recordNumber, new ArrayList<>());
putStagedRecord(recordNumber);
}
/**
* Moves the next record in the Staged state to the process state and returns its number
* @return the number of the record moved
* @throws PdbException if the record happens to be in another state (should not happen if
* in the Staged state)
*/
Integer moveFromStagedToProcessRecord() throws PdbException {
Integer record = getStagedRecord();
if (record != null) {
putProcessRecord(record);
}
return record;
}
/**
* Puts the specified record number from the Staged state to the Process state
* @param number the number of the record
* @throws PdbException if the record is not in the Staged state
*/
void moveFromStagedToProcessRecord(int number) throws PdbException {
if (!stagedRecordFifo.remove(number)) {
throw new PdbException("Number not in Staged state: " + number);
}
putProcessRecord(number);
}
/**
* Puts the specified record to the head of the Process state. If the record had been
* in the Staged state or anywhere else in the Process state, it is moved to the head of the
* Process state
* @param number the number of the record
* @throws PdbException if the records is not in the Staged state
*/
void moveToHeadProcessRecord(int number) throws PdbException {
if (stagedRecordFifo.contains(number)) {
stagedRecordFifo.remove(number);
}
else if (inProgressRecordStack.contains(number)) {
inProgressRecordStack.remove(number);
inProgressRecordStack.offerFirst(number);
}
else {
map.put(number, new ArrayList<>());
}
putProcessRecord(number);
}
/**
* Moves the specified record from the Process state to the Fixup state
* @param number the number of the record
* @param dataType the type that has been created for this in-progress type
* @throws PdbException if the record is not in the Process state
*/
void moveProcessToFixupsRecord(int number, DataType dataType) throws PdbException {
if (!inProgressRecordStack.remove(number)) {
throw new PdbException("Number not in process state: " + number);
}
if (fixupTypes.containsKey(number)) {
throw new PdbException("Number already in progress: " + number);
}
putFixupsRecord(number);
fixupTypes.put(number, dataType);
}
/**
* Removes the next record from the Fixup state and returns the number
* @return the number
*/
Integer popFixupsRecord() {
Integer record = getFixupsRecord();
if (record != null) {
if (map.containsKey(record)) {
map.remove(record);
}
if (fixupTypes.containsKey(record)) {
fixupTypes.remove(record);
}
}
return record;
}
// Not sure we will use this method
/**
* Removes the head of the Process state and returns the number. The number is not moved
* to the Fixup state
* @return the number
*/
Integer popProcessRecord() {
Integer record = getProcessRecord();
if (record != null) {
if (map.containsKey(record)) {
map.remove(record);
}
// Since pop from current, not adding to fixups
}
return record;
}
private void putStagedRecord(int record) throws PdbException {
if (stagedRecordFifo.contains(record)) {
return;
}
if (inProgressRecordStack.contains(record)) {
throw new PdbException("Record exists in another state: " + record);
}
if (fixupRecordFifo.contains(record)) {
throw new PdbException("Record exists in another state: " + record);
}
stagedRecordFifo.addFirst(record);
}
private Integer getStagedRecord() {
return stagedRecordFifo.pollLast();
}
/**
* Peeks at and returns the record number of the head of the Staged state
* @return the record number
*/
Integer peekStagedRecord() {
return stagedRecordFifo.peekLast();
}
private void putProcessRecord(int record) throws PdbException {
if (inProgressRecordStack.contains(record)) {
return;
}
if (stagedRecordFifo.contains(record)) {
throw new PdbException("Record exists in another state: " + record);
}
if (fixupRecordFifo.contains(record)) {
throw new PdbException("Record exists in another state: " + record);
}
inProgressRecordStack.addFirst(record);
}
private Integer getProcessRecord() {
return inProgressRecordStack.pollFirst();
}
private void putFixupsRecord(int record) throws PdbException {
if (fixupRecordFifo.contains(record)) {
return;
}
if (stagedRecordFifo.contains(record)) {
throw new PdbException("Record exists in another state: " + record);
}
if (inProgressRecordStack.contains(record)) {
throw new PdbException("Record exists in another state: " + record);
}
fixupRecordFifo.addFirst(record);
}
/**
* Peeks at and returns the record number of the head of the Process state
* @return the record number
*/
Integer peekProcessRecord() {
return inProgressRecordStack.peekFirst();
}
/**
* Removes and returns the record number of the head of the Fixup state
* @return the record number
*/
Integer getFixupsRecord() {
return fixupRecordFifo.pollLast();
}
/**
* Peeks at and returns the record number of the head of the Fixup state
* @return the record number
*/
Integer peekFixupsRecord() {
return fixupRecordFifo.peekLast();
}
//==============================================================================================
/**
* Puts the fixup index into the fixups for the current head of the Process state
* @param fixupIndex the fixup index
* @throws PdbException if the head of the Process state is empty or is fixups cannot be found
*/
void putFixup(int fixupIndex) throws PdbException {
List<Integer> fixups = getProcessFixups();
fixups.add(fixupIndex);
}
/**
* Returns true if the fixups for the current head of the Process state is empty
* @return {@code true} if empty
* @throws PdbException if there is no head of the Process state or its fixups cannot be found
*/
boolean processFixupsIsEmpty() throws PdbException {
List<Integer> fixups = getProcessFixups();
return fixups.isEmpty();
}
/**
* Peeks at and returns head of the Fixup state
* @return the number of the record
*/
Integer peekFixupRecord() {
return peekFixupsRecord();
}
/**
* Returns the fixups for the head of the Fixups state
* @return the fixup indices
* @throws PdbException if the head of the Fixups state does not exist or its fixups cannot be
* found
*/
List<Integer> getFixups() throws PdbException {
Integer record = peekFixupsRecord();
if (record == null) {
throw new PdbException("Empty fixups retrieval");
}
List<Integer> fixups = map.get(record);
if (fixups == null) {
throw new PdbException("Fixups not found on retrieval");
}
return fixups;
}
private List<Integer> getProcessFixups() throws PdbException {
Integer record = peekProcessRecord();
if (record == null) {
throw new PdbException("Context empty on fixups retrieval");
}
List<Integer> fixups = map.get(record);
if (fixups == null) {
throw new PdbException("Fixups not found");
}
return fixups;
}
}
@@ -132,7 +132,7 @@ public class FunctionSymbolApplier extends AbstractBlockContextApplier
function.setNoReturn(isNonReturning());
AbstractMsType fType = applicator.getPdb().getTypeRecord(typeRecordNumber);
AbstractMsType fType = applicator.getTypeRecord(typeRecordNumber);
MsTypeApplier applier = applicator.getTypeApplier(fType);
if (!(applier instanceof AbstractFunctionTypeApplier)) {
applicator.appendLogMsg("Error: Failed to resolve datatype RecordNumber " +
@@ -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,8 @@ import ghidra.app.util.SymbolPath;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Pointer;
import ghidra.util.exception.CancelledException;
/**
@@ -30,33 +31,38 @@ public class MemberFunctionTypeApplier extends AbstractFunctionTypeApplier {
// Intended for: AbstractMemberFunctionMsType
/**
* Constructor for the applicator that applies {@link AbstractMemberFunctionMsType},
* transforming it into a Ghidra {@link DataType}.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* @throws IllegalArgumentException Upon type mismatch.
* transforming it into a Ghidra {@link DataType}
* @param applicator {@link DefaultPdbApplicator} for which this class is working
* @throws IllegalArgumentException Upon type mismatch
*/
public MemberFunctionTypeApplier(DefaultPdbApplicator applicator)
throws IllegalArgumentException {
super(applicator);
}
private MsTypeApplier getThisPointerApplier(AbstractMemberFunctionMsType procType) {
MsTypeApplier applier = applicator.getTypeApplier(procType.getThisPointerRecordNumber());
return applier;
}
@Override
protected CallingConvention getCallingConvention(AbstractMsType type) {
return ((AbstractMemberFunctionMsType) type).getCallingConvention();
}
@Override
protected Pointer getThisPointer(AbstractMsType type, FixupContext fixupContext,
boolean breakCycle) throws CancelledException, PdbException {
protected RecordNumber getThisPointerRecordNumber(AbstractMsType type) {
return ((AbstractMemberFunctionMsType) type).getThisPointerRecordNumber();
}
@Override
protected RecordNumber getContainingComplexRecordNumber(AbstractMsType type) {
return ((AbstractMemberFunctionMsType) type).getContainingClassRecordNumber();
}
@Override
protected Pointer getThisPointer(AbstractMsType type)
throws CancelledException, PdbException {
RecordNumber ptrRecord = ((AbstractMemberFunctionMsType) type).getThisPointerRecordNumber();
if (ptrRecord == null) {
return null;
}
AbstractMsType mType = applicator.getPdb().getTypeRecord(ptrRecord);
AbstractMsType mType = applicator.getTypeRecord(ptrRecord);
if (mType instanceof PrimitiveMsType primitive && primitive.isNoType()) {
return null;
}
@@ -64,44 +70,28 @@ public class MemberFunctionTypeApplier extends AbstractFunctionTypeApplier {
predefineClass(msPtr.getUnderlyingRecordNumber());
}
applicator.getPdbApplicatorMetrics().witnessMemberFunctionThisPointer(mType);
DataType dt = applicator.getProcessedDataType(ptrRecord, fixupContext, breakCycle);
DataType dt = applicator.getDataType(ptrRecord);
if (dt instanceof Pointer ptr) {
return ptr;
}
return null;
}
@Override
protected Composite getContainingComplexApplier(AbstractMsType type, FixupContext fixupContext,
boolean breakCycle) throws CancelledException, PdbException {
RecordNumber containerRecord =
((AbstractMemberFunctionMsType) type).getContainingClassRecordNumber();
if (containerRecord == null) {
return null;
}
AbstractMsType mType = applicator.getPdb().getTypeRecord(containerRecord);
applicator.getPdbApplicatorMetrics().witnessMemberFunctionContainingType(mType);
DataType dt = applicator.getProcessedDataType(containerRecord, fixupContext, breakCycle);
if (dt instanceof Composite composite) {
return composite;
}
return null;
}
@Override
protected void processContainingType(AbstractMsType type) {
// TODO: evaluate whether we need to schedule this container type... guessing not
RecordNumber containerRecord =
((AbstractMemberFunctionMsType) type).getContainingClassRecordNumber();
if (containerRecord == null) {
return;
}
predefineClass(containerRecord);
AbstractMsType mType = applicator.getPdb().getTypeRecord(containerRecord);
AbstractMsType mType = applicator.getTypeRecord(containerRecord);
applicator.getPdbApplicatorMetrics().witnessMemberFunctionContainingType(mType);
}
private void predefineClass(RecordNumber recordNumber) {
AbstractMsType type = applicator.getPdb().getTypeRecord(recordNumber);
AbstractMsType type = applicator.getTypeRecord(recordNumber);
if (!(type instanceof AbstractCompositeMsType msComposite)) {
return;
}
@@ -15,30 +15,20 @@
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMemberMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractMemberMsType} types.
*/
public class MemberTypeApplier extends MsTypeApplier {
public class MemberTypeApplier extends MsDataTypeComponentApplier {
// Intended for: AbstractMemberMsType
/**
* Constructor for member type applier.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* Constructor for member type applier
* @param applicator {@link DefaultPdbApplicator} for which this class is working
*/
public MemberTypeApplier(DefaultPdbApplicator applicator) {
super(applicator);
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
// do nothing
return null;
}
}
@@ -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.
@@ -25,39 +25,30 @@ import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractModifierMsType} types.
*/
public class ModifierTypeApplier extends MsTypeApplier {
public class ModifierTypeApplier extends MsDataTypeApplier {
// Intended for: AbstractModifierMsType
/**
* Constructor for modifier type applier.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* Constructor for modifier type applier
* @param applicator {@link DefaultPdbApplicator} for which this class is working
*/
public ModifierTypeApplier(DefaultPdbApplicator applicator) {
super(applicator);
}
RecordNumber getUnderlyingNonModifierRecordNumber(RecordNumber underlyingRecord) {
return getUnderlyingNonModifierRecordNumber(applicator, underlyingRecord);
}
static RecordNumber getUnderlyingNonModifierRecordNumber(DefaultPdbApplicator applicator,
RecordNumber underlyingRecord) {
AbstractMsType underlyingType = applicator.getPdb().getTypeRecord(underlyingRecord);
while (underlyingType instanceof AbstractModifierMsType modifierType) {
RecordNumber modifiedRecord = modifierType.getModifiedRecordNumber();
underlyingType = applicator.getPdb().getTypeRecord(modifiedRecord);
}
return underlyingType.getRecordNumber();
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
AbstractModifierMsType modifierType = (AbstractModifierMsType) type;
RecordNumber modifiedRecord = modifierType.getModifiedRecordNumber();
boolean apply(AbstractMsType type) throws PdbException, CancelledException {
DataType modifiedType =
applicator.getProcessedDataType(modifiedRecord, fixupContext, false);
AbstractModifierMsType modifierMsType = (AbstractModifierMsType) type;
// Doing pre-check on type first using the getDataTypeOrSchedule method. The logic is
// simpler here for Composites or Functions because we only have one dependency type,
// so we are not doing a separate call to a pre-check method as there is in those appliers.
// If type is not available, return false.
RecordNumber modifiedRecordNumber = modifierMsType.getModifiedRecordNumber();
DataType modifiedType = applicator.getDataTypeOrSchedule(modifiedRecordNumber);
if (modifiedType == null) {
return false;
}
// If Ghidra eventually has a modified type (const, volatile) in its model, then we can
// perform the applicator.getDataType(modifierType) here, and the
@@ -75,11 +66,14 @@ public class ModifierTypeApplier extends MsTypeApplier {
// the underlying type of a Modifier to be a pointer. However, MSFT primitives include
// pointers to primitives, so in these cases we could see a const pointer to primitive
// where the const comes from the Modifier type.
DataType modifierType = modifiedType;
// if (modifiedType != null && !applicator.isPlaceholderType(modifiedType)) {
if (modifiedType != null) {
applicator.putDataType(modifierType, modifiedType);
}
return modifiedType;
// Store modified type as modifier type
applicator.putDataType(modifierMsType, modifierType);
// I'm hesitant to schedule resolve... what if I'm a pointer. Should I resolve
// modified pointers, modified other types, both, or neither? TODO; investigate
return true;
}
}
@@ -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.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.util.exception.CancelledException;
/**
* Abstract class representing the applier for a specific PDB_ID type, distinguished as
* representing an actual data type... not a component of a data type for which there is
* no associated type.
*/
public abstract class MsDataTypeApplier extends MsTypeApplier {
/**
* Constructor.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
*/
public MsDataTypeApplier(DefaultPdbApplicator applicator) {
super(applicator);
}
/**
* Apply the {@link AbstractMsType} in an attempt to create a Ghidra type
* @param type the PDB type to work on
* @return the {@code true} if type is done and can be removed from the {@code todoStack}
* @throws PdbException if there was a problem processing the data.
* @throws CancelledException upon user cancellation
*/
abstract boolean apply(AbstractMsType type) throws PdbException, CancelledException;
/**
* Returns the (long) size of the type or 0 if unknown. Or Long.MAX_VALUE if too large.
* @param type the PDB type being inspected
* @return the size; zero if unknown.
*/
long getSizeLong(AbstractMsType type) {
return applicator.bigIntegerToLong(type.getSize());
}
/**
* Returns the (int) size of the type or 0 if unknown. Or Integer.MAX_VALUE if too large.
* @param type the PDB type being inspected
* @return the size; zero if unknown.
*/
int getSizeInt(AbstractMsType type) {
return applicator.bigIntegerToInt(type.getSize());
}
//==============================================================================================
// TODO: Need to investigate if we adopt the following... if so, should use them consistently.
// /**
// * Convenience method for getting the {@link DataType} from the applicator pertaining
// * to this PDB type
// * @param type the PDB type
// * @return the ghidra data type
// */
// DataType getDataType(AbstractMsType type) {
// return applicator.getDataType(type);
// }
//
// protected int getIndex(AbstractMsType type) {
// RecordNumber recordNumber = type.getRecordNumber();
// if (recordNumber != null) {
// return recordNumber.getNumber();
// }
// return -1;
// }
}
@@ -0,0 +1,32 @@
/* ###
* 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.pdb.pdbapplicator;
/**
* Abstract class representing the applier for a specific PDB_ID type, distinguished as having
* components for an actual data type but not representing a data type in and of itself.
*/
public abstract class MsDataTypeComponentApplier extends MsTypeApplier {
/**
* Constructor.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
*/
public MsDataTypeComponentApplier(DefaultPdbApplicator applicator) {
super(applicator);
}
}
@@ -15,11 +15,9 @@
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Abstract class representing the applier for a specific PDB_ID type. The
@@ -54,54 +52,4 @@ public abstract class MsTypeApplier {
applicator.pdbLogAndInfoMessage(originator, message);
}
/**
* Apply the {@link AbstractMsType} in an attempt to create a Ghidra type.
* @param type the PDB type to work on
* @param fixupContext the fixup context to use; or pass in null during fixup process
* @param breakCycle TODO
* @return the resultant DataType
* @throws PdbException if there was a problem processing the data.
* @throws CancelledException upon user cancellation
*/
abstract DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException;
/**
* Returns the (long) size of the type or 0 if unknown. Or Long.MAX_VALUE if too large.
* @param type the PDB type being inspected
* @return the size; zero if unknown.
*/
long getSizeLong(AbstractMsType type) {
return applicator.bigIntegerToLong(type.getSize());
}
/**
* Returns the (int) size of the type or 0 if unknown. Or Integer.MAX_VALUE if too large.
* @param type the PDB type being inspected
* @return the size; zero if unknown.
*/
int getSizeInt(AbstractMsType type) {
return applicator.bigIntegerToInt(type.getSize());
}
//==============================================================================================
// TODO: Need to investigate if we adopt the following... if so, should use them consistently.
// /**
// * Convenience method for getting the {@link DataType} from the applicator pertaining
// * to this PDB type
// * @param type the PDB type
// * @return the ghidra data type
// */
// DataType getDataType(AbstractMsType type) {
// return applicator.getDataType(type);
// }
//
// protected int getIndex(AbstractMsType type) {
// RecordNumber recordNumber = type.getRecordNumber();
// if (recordNumber != null) {
// return recordNumber.getNumber();
// }
// return -1;
// }
}
@@ -0,0 +1,254 @@
/* ###
* 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.pdb.pdbapplicator;
import java.util.HashMap;
import java.util.Map;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.program.model.data.*;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Performs appropriated multiple passes on data types to get theme filled in and resolved
*/
public class MultiphaseDataTypeResolver {
private DefaultPdbApplicator applicator;
private AbstractPdb pdb;
private TaskMonitor monitor;
private RecordStack todoStack;
private RecordStack resolveStack;
public MultiphaseDataTypeResolver(DefaultPdbApplicator applicator) {
this.applicator = applicator;
this.pdb = applicator.getPdb();
this.monitor = applicator.getMonitor();
todoStack = new RecordStack();
resolveStack = new RecordStack();
}
/**
* Processes the data type associated with the record number and all dependencies of that
* type. Deals with cyclic dependencies and ultimately stores resolved (in most cases)
* types in the DefaultPdbApplicator types map
* @param recordNumber the record number
* @throws PdbException upon processing error
* @throws CancelledException upon user cancellation
*/
void process(RecordNumber recordNumber) throws PdbException, CancelledException {
// If found in the applicator map then the type is completed.
if (applicator.getDataType(recordNumber) != null) {
return;
}
// If not in the map, it will also not be in the todo or resolve stacks, as both
// should be empty at this point.
scheduleTodo(recordNumber);
RecordNumber recordToProcess;
// Peek at top of stack. If can be removed, it will be; otherwise other records can be
// pushed on top of this one for next loop cycle.
while ((recordToProcess = todoStack.peek()) != null) {
monitor.checkCancelled();
MsDataTypeApplier dataTypeApplier =
(MsDataTypeApplier) applicator.getTypeApplier(recordToProcess);
AbstractMsType msType = pdb.getTypeRecord(recordToProcess);
// If processing is done, then pop from todoStack and put onto resolveStack.
// If not completed, the do not remove from todoStack.
if (dataTypeApplier.apply(msType)) {
if (todoStack.peek() != recordToProcess) {
throw new AssertException("Top of stack violation");
}
todoStack.pop();
resolveStack.push(recordToProcess);
}
}
// Pop top of stack and work on it.
while ((recordToProcess = resolveStack.pop()) != null) {
monitor.checkCancelled();
DataType dataType = applicator.getDataType(recordToProcess);
// Resolve and re-store most data types
if (!(dataType instanceof PointerDataType || dataType instanceof BitFieldDataType)) {
dataType = applicator.resolve(dataType);
applicator.putDataType(recordToProcess, dataType);
}
}
}
/**
* Method used to schedule another type (indicated by the record number). This scheduled
* type is put on top of a stack of types to process, pushing what was the current type
* being processed down. If the type indicated by the record number is already on the stack,
* it is lifted to the top of the stack. Note that composite types (by virtue of the fact
* that impls for these are created and stored in the applicator map, but not filled in) will
* not be lifted to the top of the stack. This prevents oscillation on the stack and also
* is the mechanism by which dependency cycles are broken
* @param recordNumber the record number to be scheduled
*/
void scheduleTodo(RecordNumber recordNumber) {
MsTypeApplier applier = applicator.getTypeApplier(recordNumber);
if (!(applier instanceof MsDataTypeApplier dataTypeApplier)) {
// Return without scheduling... only want to schedule that that have a legitimate
// data type to store
return;
}
todoStack.push(recordNumber);
}
/**
* Stack of record numbers with O(1) push/pop, O(1) contains, O(1) removal from
* anywhere, and thus O(1) move from anywhere to top. These nodes hold the RecordNumbers
* that are being scheduled
*/
static class RecordStack {
static class RecordNode {
RecordNode next;
RecordNode prev;
RecordNumber recordNumber;
/**
* Create new node for the record number. Note that the {@code next} and {@code prev}
* values are not set. They must be set by the RecordStack.
* @param recordNumber the record number
*/
private RecordNode(RecordNumber recordNumber) {
this.recordNumber = recordNumber;
}
}
static final RecordNumber HEAD = RecordNumber.typeRecordNumber(-1);
static final RecordNumber TAIL = RecordNumber.typeRecordNumber(-2);
Map<RecordNumber, RecordNode> map;
RecordNode head;
RecordNode tail;
/**
* Constructor for new record stack
*/
RecordStack() {
// head and tail are not put into the map
map = new HashMap<>();
head = new RecordNode(HEAD);
tail = new RecordNode(TAIL);
head.next = null;
head.prev = tail;
tail.next = head;
tail.prev = null;
}
/**
* Indicates if number number exists on stack
* @param recordNumber the record number to check
* @return {@code true} if exists
*/
boolean contains(RecordNumber recordNumber) {
return map.containsKey(recordNumber);
}
/**
* Pushes the record number onto the top of the stack. If the record number was already
* on the stack, it is moved to the top
* @param recordNumber the record number to push
*/
void push(RecordNumber recordNumber) {
RecordNode node = getNode(recordNumber);
if (node == head.prev) {
return; // already on top of stack
}
if (node == null) {
node = new RecordNode(recordNumber);
map.put(recordNumber, node);
}
else { // already exists in non-top-of-stack position
removeNodeLinkage(node);
}
insertNodeLinkage(head, node);
}
/**
* Peek at top node
* @return the node's record number or {@code null} if if no nodes left
*/
RecordNumber peek() {
RecordNode node = getTop();
if (node == tail) {
return null;
}
return node.recordNumber;
}
/**
* Pop top node
* @return the popped node's record number or {@code null} if if no nodes left
*/
RecordNumber pop() {
RecordNode node = getTop();
if (node == tail) {
return null;
}
removeNodeLinkage(node);
map.remove(node.recordNumber);
return node.recordNumber;
}
/**
* Get node for the record number
* @return the node
*/
private RecordNode getNode(RecordNumber recordNumber) {
return map.get(recordNumber);
}
/**
* Get top node
* @return the node
*/
private RecordNode getTop() {
return head.prev;
}
/**
* Add node to the stack. Gets placed below locationNode, which can be head for pushing
* onto the stack
*/
private void insertNodeLinkage(RecordNode locationNode, RecordNode newNode) {
newNode.next = locationNode;
newNode.prev = locationNode.prev;
locationNode.prev.next = newNode;
locationNode.prev = newNode;
}
/**
* Remove node from bidirectional linkage
* @param node the node to remove; must not be {@code head}, {@code tail}, or {@code null}
*/
private void removeNodeLinkage(RecordNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
node.prev = null;
node.next = null;
}
}
}
@@ -15,16 +15,12 @@
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractNestedTypeMsType} and {@link AbstractNestedTypeExtMsType} types.
*/
public class NestedTypeApplier extends MsTypeApplier {
public class NestedTypeApplier extends MsDataTypeComponentApplier {
// Intended for: AbstractNestedTypeMsType or AbstractNestedTypeExtMsType
/**
@@ -53,25 +49,6 @@ public class NestedTypeApplier extends MsTypeApplier {
return "";
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
RecordNumber typeRecordNumber;
if (type instanceof AbstractNestedTypeMsType nestedType) {
typeRecordNumber = nestedType.getNestedTypeDefinitionRecordNumber();
}
else if (type instanceof AbstractNestedTypeExtMsType extNestedType) {
typeRecordNumber = extNestedType.getNestedTypeDefinitionRecordNumber();
}
else {
throw new PdbException(
"Unexpected nested type in field list: " + type.getClass().getSimpleName());
}
AbstractMsType mType = applicator.getPdb().getTypeRecord(typeRecordNumber);
MsTypeApplier applier = applicator.getTypeApplier(typeRecordNumber);
return applier.apply(mType, fixupContext, breakCycle);
}
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractNestedTypeMsType) &&
@@ -81,4 +58,5 @@ public class NestedTypeApplier extends MsTypeApplier {
}
return type;
}
}
@@ -15,11 +15,6 @@
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Used for creating a wrapper for when there is not associated type to the PDB type (or if we
* have not yet created the association).
@@ -37,10 +32,4 @@ public class NoTypeApplier extends MsTypeApplier {
super(applicator);
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
// do nothing
return null;
}
}
@@ -378,10 +378,12 @@ public class PdbResearch {
for (int indexNumber : developerDebugOrderIndexNumbers) {
monitor.checkCancelled();
PdbResearch.checkBreak(indexNumber);
FixupContext fixupContext = new FixupContext();
fixupContext.addStagedRecord(indexNumber);
applicator.getProcessedDataType(RecordNumber.typeRecordNumber(indexNumber),
fixupContext, true);
//20240214: neutered internals... containing method not being used at the moment...
// consider what needs to be done below
// FixupContext fixupContext = new FixupContext();
// fixupContext.addStagedRecord(indexNumber);
// applicator.getProcessedDataType(RecordNumber.typeRecordNumber(indexNumber),
// fixupContext, true);
}
}
@@ -426,7 +428,7 @@ public class PdbResearch {
MsSymbolApplier applier = applicator.getSymbolApplier(iter);
if (applier instanceof TypedefSymbolApplier typedefApplier) {
RecordNumber typeNumber = typedefApplier.getTypeRecordNumber();
AbstractMsType type = applicator.getPdb().getTypeRecord(typeNumber);
AbstractMsType type = applicator.getTypeRecord(typeNumber);
System.out
.println(
"UDT " + typedefApplier.getName() + " depends on " + type.toString());
@@ -21,22 +21,21 @@ import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractPointerMsType.MsPointerMode;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractPointerMsType} types.
*/
public class PointerTypeApplier extends MsTypeApplier {
public class PointerTypeApplier extends MsDataTypeApplier {
// Intended for: AbstractPointerMsType
/**
* Constructor for pointer type applier, for transforming a enum into a
* Ghidra DataType.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* @throws IllegalArgumentException Upon invalid arguments.
* Constructor for pointer type applier, for transforming a enum into a Ghidra DataType
* @param applicator {@link DefaultPdbApplicator} for which this class is working
* @throws IllegalArgumentException Upon invalid arguments
*/
public PointerTypeApplier(DefaultPdbApplicator applicator)
throws IllegalArgumentException {
public PointerTypeApplier(DefaultPdbApplicator applicator) throws IllegalArgumentException {
super(applicator);
}
@@ -45,15 +44,14 @@ public class PointerTypeApplier extends MsTypeApplier {
* if we develop member pointers into the Ghidra framework; this method exists to pass some
* pertinent information along to the user
* @param type the PDB type being inspected
* @param fixupContext the fixup context to use; or pass in null during fixup process
* @return comment string or null
* @throws CancelledException upon user cancellation
* @throws PdbException upon processing error
*/
String getPointerCommentField(AbstractPointerMsType type, FixupContext fixupContext)
String getPointerCommentField(AbstractPointerMsType type)
throws CancelledException, PdbException {
AbstractPointerMsType.MsPointerMode pointerMode = type.getPointerMode();
if (pointerMode == AbstractPointerMsType.MsPointerMode.MEMBER_FUNCTION_POINTER) {
AbstractPointerMsType.MsPointerMode msPointerMode = type.getPointerMode();
if (msPointerMode == AbstractPointerMsType.MsPointerMode.MEMBER_FUNCTION_POINTER) {
// We are no longer able to get underlying type in time due to cycle breaks unless
// we start doing fixups on pmf/pdm pointers.
// TODO: consider fixups on these later... maybe after we understand contents of
@@ -63,7 +61,7 @@ public class PointerTypeApplier extends MsTypeApplier {
//return "\"::*\" (pmf) to type: " + underlyingType;
return "\"::*\" (pmf)";
}
else if (pointerMode == AbstractPointerMsType.MsPointerMode.MEMBER_DATA_POINTER) {
else if (msPointerMode == AbstractPointerMsType.MsPointerMode.MEMBER_DATA_POINTER) {
// We are no longer able to get underlying type in time due to cycle breaks unless
// we start doing fixups on pmf/pdm pointers.
// TODO: consider fixups on these later... maybe after we understand contents of
@@ -77,56 +75,57 @@ public class PointerTypeApplier extends MsTypeApplier {
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
boolean apply(AbstractMsType type) throws PdbException, CancelledException {
AbstractPointerMsType pointerMsType = (AbstractPointerMsType) type;
// Doing pre-check on type first using the getDataTypeOrSchedule method. The logic is
// simpler here for Composites or Functions because we only have one dependency type,
// so we are not doing a separate call to a pre-check method as there is in those appliers.
// If type is not available, return false.
RecordNumber underlyingRecordNumber = pointerMsType.getUnderlyingRecordNumber();
DataType underlyingType = applicator.getDataTypeOrSchedule(underlyingRecordNumber);
if (underlyingType == null) {
return false;
}
DataType dataType;
if (fixupContext != null) {
// The next line will only return null until we start putting in a DB version of the
// pointer, below. Need to work that out. TODO: take care of this
dataType = applicator.getDataType(type);
if (dataType != null) {
return dataType;
}
}
if (type instanceof DummyMsType) {
dataType = new PointerDataType(applicator.getDataTypeManager());
}
else {
dataType = applyAbstractPointerMsType((AbstractPointerMsType) type, fixupContext);
AbstractPointerMsType.MsPointerMode msPointerMode = pointerMsType.getPointerMode();
switch (msPointerMode) {
case POINTER:
dataType = processPointer(pointerMsType, underlyingType);
case LVALUE_REFERENCE:
dataType = processReference(pointerMsType, underlyingType);
case RVALUE_REFERENCE:
dataType = processReference(pointerMsType, underlyingType);
case MEMBER_DATA_POINTER:
case MEMBER_FUNCTION_POINTER:
dataType = processMemberPointer(pointerMsType, underlyingType);
case INVALID:
case RESERVED:
Msg.warn(this, "Unable to process PointerMode: " + msPointerMode +
". Using default Pointer.");
dataType = PointerDataType.dataType;
default:
throw new PdbException("PDB Error: unhandled PointerMode in " + getClass());
}
}
dataType = applicator.resolve(dataType);
applicator.putDataType(type, dataType);
return dataType;
return true;
}
private DataType getUnderlyingType(AbstractPointerMsType type, FixupContext fixupContext)
throws CancelledException, PdbException {
RecordNumber underlyingRecord = type.getUnderlyingRecordNumber();
return applicator.getProcessedDataType(underlyingRecord, fixupContext, true);
}
private DataType processMemberPointer(AbstractPointerMsType type, DataType underlyingType) {
private DataType applyAbstractPointerMsType(AbstractPointerMsType type,
FixupContext fixupContext) throws CancelledException, PdbException {
AbstractPointerMsType.MsPointerMode pointerMode = type.getPointerMode();
if (pointerMode == AbstractPointerMsType.MsPointerMode.MEMBER_DATA_POINTER ||
pointerMode == AbstractPointerMsType.MsPointerMode.MEMBER_FUNCTION_POINTER) {
return processMemberPointer(type, fixupContext);
}
return processPointer(type, fixupContext);
}
private DataType processMemberPointer(AbstractPointerMsType type, FixupContext fixupContext)
throws CancelledException, PdbException {
// future use
DataType underlyingType = getUnderlyingType(type, fixupContext);
int size = type.getSize().intValueExact();
String name;
AbstractPointerMsType.MsPointerMode pointerMode = type.getPointerMode();
if (pointerMode == AbstractPointerMsType.MsPointerMode.MEMBER_FUNCTION_POINTER) {
AbstractPointerMsType.MsPointerMode msPointerMode = type.getPointerMode();
if (msPointerMode == AbstractPointerMsType.MsPointerMode.MEMBER_FUNCTION_POINTER) {
name = String.format("pmf_%08x", type.toString().hashCode());
}
else {
@@ -139,12 +138,11 @@ public class PointerTypeApplier extends MsTypeApplier {
DataType dt = new StructureDataType(storagePath, name, size);
dt.setDescription(type.toString());
return applicator.resolve(dt);
return dt;
}
private CategoryPath getCategoryPathForMemberPointer(RecordNumber containingClassRecordNumber) {
AbstractMsType containingType =
applicator.getPdb().getTypeRecord(containingClassRecordNumber);
AbstractMsType containingType = applicator.getTypeRecord(containingClassRecordNumber);
MsTypeApplier applier = applicator.getTypeApplier(containingClassRecordNumber);
if (containingType instanceof AbstractCompositeMsType compositeMsType &&
applier instanceof CompositeTypeApplier compositeApplier) {
@@ -155,9 +153,7 @@ public class PointerTypeApplier extends MsTypeApplier {
return applicator.getAnonymousTypesCategory();
}
private DataType processPointer(AbstractPointerMsType type, FixupContext fixupContext)
throws CancelledException, PdbException {
DataType underlyingType = getUnderlyingType(type, fixupContext);
private DataType processPointer(AbstractPointerMsType type, DataType underlyingType) {
int size = type.getSize().intValueExact();
if (size > PointerDataType.MAX_POINTER_SIZE_BYTES) {
return getStubPointer(type);
@@ -165,21 +161,28 @@ public class PointerTypeApplier extends MsTypeApplier {
if (size == applicator.getDataOrganization().getPointerSize()) {
size = -1; // Use default
}
if (underlyingType == null || applicator.isPlaceholderType(underlyingType)) {
return applicator.getPlaceholderPointer(size);
return new PointerDataType(underlyingType, size, applicator.getDataTypeManager());
}
private DataType processReference(AbstractPointerMsType type, DataType underlyingType) {
int size = type.getSize().intValueExact();
if (size > PointerDataType.MAX_POINTER_SIZE_BYTES) {
return getStubPointer(type);
}
if (size == applicator.getDataOrganization().getPointerSize()) {
size = -1; // Use default
}
return new PointerDataType(underlyingType, size, applicator.getDataTypeManager());
}
private DataType getStubPointer(AbstractPointerMsType type) {
int size = type.getSize().intValueExact();
AbstractMsType under = applicator.getPdb().getTypeRecord(type.getUnderlyingRecordNumber());
AbstractMsType under = applicator.getTypeRecord(type.getUnderlyingRecordNumber());
CategoryPath categoryPath = applicator.getAnonymousTypesCategory();
MsPointerMode mode = type.getPointerMode();
MsPointerMode msPointerMode = type.getPointerMode();
AbstractPointerMsType.PointerType pt = type.getPointerType();
String name =
String.format("StubPtr%d_%s%s_To_%s", 8 * size, pt.toString(), mode.toString(),
under.getName());
String name = String.format("StubPtr%d_%s%s_To_%s", 8 * size, pt.toString(),
msPointerMode.toString(), under.getName());
DataType stubPtr = new StructureDataType(categoryPath, name, size);
return stubPtr;
}
@@ -16,6 +16,7 @@
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.PrimitiveMsType;
import ghidra.program.model.data.DataType;
@@ -24,22 +25,20 @@ import ghidra.util.exception.CancelledException;
/**
* Applier for {@link PrimitiveMsType} types.
*/
public class PrimitiveTypeApplier extends MsTypeApplier {
public class PrimitiveTypeApplier extends MsDataTypeApplier {
// Intended for: PrimitiveMsType
/**
* Constructor for primitive type applier, for transforming a primitive into a
* Ghidra DataType.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* Constructor for primitive type applier, for transforming a primitive into a Ghidra DataType
* @param applicator {@link DefaultPdbApplicator} for which this class is working
*/
public PrimitiveTypeApplier(DefaultPdbApplicator applicator) {
super(applicator);
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
return applyPrimitiveMsType((PrimitiveMsType) type);
boolean apply(AbstractMsType type) throws PdbException, CancelledException {
return (applyPrimitiveMsType((PrimitiveMsType) type) != null);
}
boolean isNoType(AbstractMsType type) {
@@ -59,7 +58,9 @@ public class PrimitiveTypeApplier extends MsTypeApplier {
// }
int indexNumber = type.getNumber();
DataType existingDt = applicator.getDataType(indexNumber);
RecordNumber recordNumber = RecordNumber.typeRecordNumber(indexNumber);
DataType existingDt = applicator.getDataType(recordNumber);
if (existingDt != null) {
return existingDt;
}
@@ -2062,8 +2063,11 @@ public class PrimitiveTypeApplier extends MsTypeApplier {
}
// Doing a direct and immediate resolve (not scheduling... the type would never get into
// the processing queue because primitive type numbers are not found in the sequential
// set of record numbers, but they are referred to by non-primitive types).
primitiveDataType = applicator.resolve(primitiveDataType);
applicator.putDataType(indexNumber, primitiveDataType);
applicator.putDataType(recordNumber, primitiveDataType);
return primitiveDataType;
}
@@ -18,7 +18,8 @@ package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Pointer;
import ghidra.util.exception.CancelledException;
/**
@@ -29,9 +30,9 @@ public class ProcedureTypeApplier extends AbstractFunctionTypeApplier {
// Intended for: AbstractProcedureMsType
/**
* Constructor for the applicator that applies {@link AbstractProcedureMsType},
* transforming it into a Ghidra {@link DataType}.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* @throws IllegalArgumentException Upon invalid arguments.
* transforming it into a Ghidra {@link DataType}
* @param applicator {@link DefaultPdbApplicator} for which this class is working
* @throws IllegalArgumentException Upon invalid arguments
*/
public ProcedureTypeApplier(DefaultPdbApplicator applicator) throws IllegalArgumentException {
super(applicator);
@@ -43,14 +44,17 @@ public class ProcedureTypeApplier extends AbstractFunctionTypeApplier {
}
@Override
protected Pointer getThisPointer(AbstractMsType type, FixupContext fixupContext,
boolean breakCycle) throws CancelledException, PdbException {
protected RecordNumber getThisPointerRecordNumber(AbstractMsType type) {
return null;
}
@Override
protected Composite getContainingComplexApplier(AbstractMsType type, FixupContext fixupContext,
boolean breakCycle) throws CancelledException, PdbException {
protected RecordNumber getContainingComplexRecordNumber(AbstractMsType type) {
return null;
}
@Override
protected Pointer getThisPointer(AbstractMsType type) throws CancelledException, PdbException {
return null;
}
@@ -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.
@@ -18,8 +18,7 @@ package ghidra.app.util.pdb.pdbapplicator;
import java.util.HashMap;
import java.util.Map;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
/**
@@ -30,10 +29,12 @@ import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
public class TypeApplierFactory {
private DefaultPdbApplicator applicator;
private AbstractPdb pdb;
private Map<Integer, MsTypeApplier> appliersByPdbId;
TypeApplierFactory(DefaultPdbApplicator applicator) {
this.applicator = applicator;
this.pdb = applicator.getPdb();
appliersByPdbId = new HashMap<>();
}
@@ -51,7 +52,7 @@ public class TypeApplierFactory {
MsTypeApplier getApplierOrNoTypeSpec(RecordNumber recordNumber,
Class<? extends MsTypeApplier> expected) throws PdbException {
MsTypeApplier applier = getTypeApplier(recordNumber);
AbstractMsType type = applicator.getPdb().getTypeRecord(recordNumber);
AbstractMsType type = pdb.getTypeRecord(recordNumber);
if (!expected.isInstance(applier)) {
if (!(applier instanceof PrimitiveTypeApplier &&
((PrimitiveTypeApplier) applier).isNoType(type))) {
@@ -63,7 +64,7 @@ public class TypeApplierFactory {
}
MsTypeApplier getTypeApplier(RecordNumber recordNumber) {
return getTypeApplier(applicator.getPdb().getTypeRecord(recordNumber));
return getTypeApplier(pdb.getTypeRecord(recordNumber));
}
//==============================================================================================
@@ -91,7 +91,7 @@ public class TypedefSymbolApplier extends MsSymbolApplier
String name = symbol.getName();
RecordNumber typeRecordNumber = symbol.getTypeRecordNumber();
AbstractMsType mType = applicator.getPdb().getTypeRecord(typeRecordNumber);
AbstractMsType mType = applicator.getTypeRecord(typeRecordNumber);
MsTypeApplier applier = applicator.getTypeApplier(typeRecordNumber);
// TODO:... NOT SURE IF WILL ALWAYS BE A DATATYPE OR WILL BE A VARIABLE OR ????
if (applier == null) {
@@ -15,11 +15,8 @@
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractBaseClassMsType}, {@link AbstractVirtualBaseClassMsType}, and
@@ -83,22 +80,23 @@ public class UdtSourceLineTypeApplier extends MsTypeApplier {
return null;
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
String filename = getSourceFileName(type);
int lineNumber = getLineNumber(type);
RecordNumber udtRecordNumber = getUdtRecordNumber(type);
MsTypeApplier typeApplier = applicator.getTypeApplier(udtRecordNumber);
// do nothing at the moment.
applicator.putRecordNumberByFileName(udtRecordNumber, filename);
if (type instanceof UserDefinedTypeModuleSourceAndLineMsType) {
int moduleNumber = ((UserDefinedTypeModuleSourceAndLineMsType) type).getModuleNumber();
applicator.putRecordNumberByModuleNumber(udtRecordNumber, moduleNumber);
}
return null;
}
// Keeping as commented out for now so to have something to start with for this ITEM type
// @Override
// DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
// throws PdbException, CancelledException {
// String filename = getSourceFileName(type);
// int lineNumber = getLineNumber(type);
// RecordNumber udtRecordNumber = getUdtRecordNumber(type);
// MsTypeApplier typeApplier = applicator.getTypeApplier(udtRecordNumber);
//
// // do nothing at the moment.
// applicator.putRecordNumberByFileName(udtRecordNumber, filename);
// if (type instanceof UserDefinedTypeModuleSourceAndLineMsType) {
// int moduleNumber = ((UserDefinedTypeModuleSourceAndLineMsType) type).getModuleNumber();
// applicator.putRecordNumberByModuleNumber(udtRecordNumber, moduleNumber);
// }
// return null;
// }
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
@@ -15,24 +15,19 @@
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractVirtualFunctionTablePointerMsType} and
* {@link AbstractVirtualFunctionTablePointerWithOffsetMsType} types.
*/
public class VirtualFunctionTablePointerTypeApplier extends MsTypeApplier {
public class VirtualFunctionTablePointerTypeApplier extends MsDataTypeComponentApplier {
// Intended for: AbstractVirtualFunctionTablePointerMsType or
// AbstractVirtualFunctionTablePointerWithOffsetMsType
/**
* Constructor for enum type applier, for transforming a enum into a
* Ghidra DataType.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* Constructor for enum type applier, for transforming a enum into a Ghidra DataType
* @param applicator {@link DefaultPdbApplicator} for which this class is working
* @throws IllegalArgumentException Upon invalid arguments.
*/
public VirtualFunctionTablePointerTypeApplier(DefaultPdbApplicator applicator)
@@ -56,34 +51,7 @@ public class VirtualFunctionTablePointerTypeApplier extends MsTypeApplier {
return "VFTablePtr" + getOffset(type);
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
// usually no record number, so cannot retrieve or store from/to applicator
AbstractVirtualFunctionTablePointerMsType vftPtrType = validateType(type);
RecordNumber recordNumber = vftPtrType.getPointerTypeRecordNumber();
DataType dataType = applyPointer(recordNumber, fixupContext, breakCycle);
// unlike regular pointer, we are resolving vft pointer
dataType = applicator.resolve(dataType);
return dataType;
}
private DataType applyPointer(RecordNumber pointerTypeRecordNumber, FixupContext fixupContext,
boolean breakCycle) throws CancelledException, PdbException {
MsTypeApplier rawApplier = applicator.getTypeApplier(pointerTypeRecordNumber);
if (rawApplier instanceof PointerTypeApplier pointerApplier) {
AbstractMsType type = applicator.getPdb().getTypeRecord(pointerTypeRecordNumber);
return pointerApplier.apply(type, fixupContext, breakCycle);
}
applicator.appendLogMsg("cannot process " + rawApplier.getClass().getSimpleName() + "for " +
getClass().getSimpleName());
return null;
}
private static AbstractVirtualFunctionTablePointerMsType validateType(AbstractMsType type)
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractVirtualFunctionTablePointerMsType vftPtrType)) {
throw new IllegalArgumentException(
@@ -28,12 +28,12 @@ import ghidra.util.exception.CancelledException;
/**
* Applier for {@link VtShapeMsType} types.
*/
public class VtShapeTypeApplier extends MsTypeApplier {
public class VtShapeTypeApplier extends MsDataTypeApplier {
// Intended for: VtShapeMsType
/**
* Constructor for vtshape type applier.
* @param applicator {@link DefaultPdbApplicator} for which this class is working.
* Constructor for vtshape type applier
* @param applicator {@link DefaultPdbApplicator} for which this class is working
*/
public VtShapeTypeApplier(DefaultPdbApplicator applicator) {
super(applicator);
@@ -49,16 +49,16 @@ public class VtShapeTypeApplier extends MsTypeApplier {
}
@Override
DataType apply(AbstractMsType type, FixupContext fixupContext, boolean breakCycle)
throws PdbException, CancelledException {
boolean apply(AbstractMsType type) throws PdbException, CancelledException {
// Note that focused investigation as shown that both the VTShape as well as the pointer
// to the particular VTShapes are not specific to one class; they can be shared by
// totally unrelated classes; moreover, no duplicates of any VTShape or pointer to a
// particular VTShape were found either. Because of this, for now, the VTShape is going
// into an anonymous types category.
DataType dataType = createVtShape((VtShapeMsType) type);
// return applicator.resolve(dataType);
return dataType;
applicator.putDataType(type, dataType);
return true;
}
// We are creating a structure for the vtshape.
@@ -133,4 +133,5 @@ public class VtShapeTypeApplier extends MsTypeApplier {
}
return shape; // not resolved
}
}