private static void CompareTypes(IDiaSymbol diaType, PdbType pdbType, HashSet <Tuple <uint, PdbType> > checkedTypes) { if (diaType == null) { Assert.True(pdbType.TypeIndex.IsNoneType); return; } uint diaSymbolId = diaType.symIndexId; var checkedTypesTuple = Tuple.Create(diaSymbolId, pdbType); if (checkedTypes.Contains(checkedTypesTuple)) { return; } checkedTypes.Add(checkedTypesTuple); Assert.Equal(diaType.length, pdbType.Size); Assert.Equal(diaType.constType, pdbType.ModifierOptions.HasFlag(ModifierOptions.Const)); Assert.Equal(diaType.unalignedType, pdbType.ModifierOptions.HasFlag(ModifierOptions.Unaligned)); Assert.Equal(diaType.volatileType, pdbType.ModifierOptions.HasFlag(ModifierOptions.Volatile)); if (diaType.symTag == SymTagEnum.UDT) { Assert.IsAssignableFrom <PdbUserDefinedType>(pdbType); PdbUserDefinedType pdbUdt = (PdbUserDefinedType)pdbType; if (pdbType.Name != "<unnamed-tag>") { Assert.Equal(diaType.name, pdbType.Name); } Assert.Equal(diaType.nested, pdbUdt.IsNested); Assert.Equal(diaType.scoped, pdbUdt.IsScoped); Assert.Equal(diaType.packed, pdbUdt.IsPacked); Assert.Equal(diaType.overloadedOperator, pdbUdt.HasOverloadedOperator); Assert.Equal(diaType.hasNestedTypes, pdbUdt.ContainsNestedClass); Assert.Equal(diaType.hasAssignmentOperator, pdbUdt.HasOverloadedAssignmentOperator); Assert.Equal(diaType.constructor, pdbUdt.HasConstructorOrDestructor); // Fields var diaData = diaType.GetChildren(SymTagEnum.Data).ToArray(); var diaFields = diaData.Where(d => d.locationType == LocationType.ThisRel || d.locationType == LocationType.BitField).ToArray(); var pdbFields = pdbUdt.Fields; Assert.Equal(diaFields.Length, pdbFields.Count); for (int i = 0; i < pdbFields.Count; i++) { if (pdbFields[i] is PdbTypeBitField bitField) { Assert.Equal(LocationType.BitField, diaFields[i].locationType); Assert.Equal(diaFields[i].bitPosition, bitField.BitOffset); Assert.Equal(diaFields[i].length, bitField.BitSize); } Assert.Equal(diaFields[i].access, (uint)pdbFields[i].Access); Assert.Equal(diaFields[i].name, pdbFields[i].Name); Assert.Equal((ulong)diaFields[i].offset, pdbFields[i].Offset); CompareTypes(diaFields[i].type, pdbFields[i].Type, checkedTypes); } // Constants var diaConstants = diaData.Where(d => d.locationType == LocationType.Constant).ToArray(); var pdbConstants = pdbUdt.StaticFields.OfType <PdbTypeConstant>().ToArray(); Assert.Equal(diaConstants.Length, pdbConstants.Length); for (int i = 0; i < pdbConstants.Length; i++) { Assert.Equal(diaConstants[i].access, (uint)pdbConstants[i].Access); Assert.Equal(diaConstants[i].name, pdbConstants[i].Name); Assert.Equal(diaConstants[i].value.ToString(), pdbConstants[i].Value.ToString()); CompareTypes(diaConstants[i].type, pdbConstants[i].Type, checkedTypes); } // Thread local storage var diaTls = diaData.Where(d => d.locationType == LocationType.TLS).ToArray(); var pdbTls = pdbUdt.StaticFields.OfType <PdbTypeThreadLocalStorage>().ToArray(); Assert.Equal(diaTls.Length, pdbTls.Length); for (int i = 0; i < pdbTls.Length; i++) { Assert.Equal(diaTls[i].access, (uint)pdbTls[i].Access); Assert.Equal(diaTls[i].name, pdbTls[i].Name); Assert.Equal(diaTls[i].addressSection, pdbTls[i].Segment); Assert.Equal(diaTls[i].addressOffset, pdbTls[i].Offset); CompareTypes(diaTls[i].type, pdbTls[i].Type, checkedTypes); } // Static fields var diaStaticFields = diaData.Where(d => d.locationType == LocationType.Static).ToArray(); var pdbStaticFields = pdbUdt.StaticFields.Where(sf => !(sf is PdbTypeConstant) && !(sf is PdbTypeThreadLocalStorage)).ToArray(); Assert.Equal(diaStaticFields.Length, pdbStaticFields.Length); for (int i = 0; i < pdbStaticFields.Length; i++) { Assert.Equal(diaStaticFields[i].access, (uint)pdbStaticFields[i].Access); Assert.Equal(diaStaticFields[i].name, pdbStaticFields[i].Name); if (pdbStaticFields[i] is PdbTypeRegularStaticField regularStaticField) { Assert.Equal(diaStaticFields[i].addressSection, regularStaticField.Segment); Assert.Equal(diaStaticFields[i].addressOffset, regularStaticField.Offset); Assert.Equal(diaStaticFields[i].relativeVirtualAddress, regularStaticField.RelativeVirtualAddress); } CompareTypes(diaStaticFields[i].type, pdbStaticFields[i].Type, checkedTypes); } // Base classes var diaAllBaseClasses = diaType.GetChildren(SymTagEnum.BaseClass).Concat(diaType.GetChildren(SymTagEnum.BaseInterface)).ToArray(); var diaBaseClasses = diaAllBaseClasses.Where(b => !b.virtualBaseClass).ToArray(); Assert.Equal(diaBaseClasses.Length, pdbUdt.BaseClasses.Count); for (int i = 0; i < diaBaseClasses.Length; i++) { Assert.Equal(diaBaseClasses[i].access, (uint)pdbUdt.BaseClasses[i].Access); Assert.Equal(diaBaseClasses[i].offset, (int)pdbUdt.BaseClasses[i].Offset); CompareTypes(diaBaseClasses[i].type, pdbUdt.BaseClasses[i].BaseType, checkedTypes); } // Virtual base classes var diaVirtualBaseClasses = diaAllBaseClasses.Where(b => b.virtualBaseClass).ToArray(); Assert.Equal(diaVirtualBaseClasses.Length, pdbUdt.VirtualBaseClasses.Count); for (int i = 0; i < diaVirtualBaseClasses.Length; i++) { Assert.Equal(diaVirtualBaseClasses[i].access, (uint)pdbUdt.VirtualBaseClasses[i].Access); Assert.Equal(diaVirtualBaseClasses[i].virtualBaseDispIndex, (uint)pdbUdt.VirtualBaseClasses[i].VirtualTableIndex); Assert.Equal(diaVirtualBaseClasses[i].virtualBasePointerOffset, (int)pdbUdt.VirtualBaseClasses[i].VirtualBasePointerOffset); CompareTypes(diaVirtualBaseClasses[i].type, pdbUdt.VirtualBaseClasses[i].BaseType, checkedTypes); CompareTypes(diaVirtualBaseClasses[i].virtualBaseTableType, pdbUdt.VirtualBaseClasses[i].VirtualBasePointerType, checkedTypes); } } else if (diaType.symTag == SymTagEnum.Enum) { Assert.IsAssignableFrom <PdbEnumType>(pdbType); PdbEnumType pdbEnumType = (PdbEnumType)pdbType; Assert.Equal(diaType.name, pdbType.Name); Assert.Equal(diaType.nested, pdbEnumType.IsNested); Assert.Equal(diaType.scoped, pdbEnumType.IsScoped); Assert.Equal(diaType.packed, pdbEnumType.IsPacked); Assert.Equal(diaType.overloadedOperator, pdbEnumType.HasOverloadedOperator); Assert.Equal(diaType.hasNestedTypes, pdbEnumType.ContainsNestedClass); Assert.Equal(diaType.hasAssignmentOperator, pdbEnumType.HasOverloadedAssignmentOperator); Assert.Equal(diaType.constructor, pdbEnumType.HasConstructorOrDestructor); CompareBaseType(diaType.type, pdbEnumType.UnderlyingType); CompareBaseType(diaType, pdbEnumType.UnderlyingType); // Compare enumeration values var diaValues = diaType.GetChildren(SymTagEnum.Data).ToArray(); Assert.Equal(diaValues.Length, pdbEnumType.Values.Count); for (int i = 0; i < diaValues.Length; i++) { Assert.Equal(DataKind.Constant, diaValues[i].dataKind); Assert.Equal(diaValues[i].name, pdbEnumType.Values[i].Name); Assert.Equal(diaValues[i].value.ToString(), pdbEnumType.Values[i].Value.ToString()); } } else if (diaType.symTag == SymTagEnum.BaseType) { CompareBaseType(diaType, pdbType); } else if (diaType.symTag == SymTagEnum.ArrayType) { Assert.IsAssignableFrom <PdbArrayType>(pdbType); PdbArrayType pdbArrayType = (PdbArrayType)pdbType; Assert.Equal(diaType.count, pdbArrayType.Count); CompareTypes(diaType.type, pdbArrayType.ElementType, checkedTypes); CompareTypes(diaType.arrayIndexType, pdbArrayType.IndexType, checkedTypes); } else if (diaType.symTag == SymTagEnum.PointerType) { Assert.IsAssignableFrom <PdbPointerType>(pdbType); PdbPointerType pdbPointerType = (PdbPointerType)pdbType; Assert.Equal(diaType.reference, pdbPointerType.IsLValueReference); Assert.Equal(diaType.RValueReference, pdbPointerType.IsRValueReference); CompareTypes(diaType.type, pdbPointerType.ElementType, checkedTypes); } else if (diaType.symTag == SymTagEnum.FunctionType) { PdbType returnType; CallingConvention callingConvention; ushort parameterCount; PdbType[] pdbArguments; if (pdbType is PdbFunctionType) { Assert.IsAssignableFrom <PdbFunctionType>(pdbType); PdbFunctionType pdbFunctionType = (PdbFunctionType)pdbType; returnType = pdbFunctionType.ReturnType; callingConvention = pdbFunctionType.CallingConvention; parameterCount = pdbFunctionType.ParameterCount; pdbArguments = pdbFunctionType.Arguments; } else { Assert.IsAssignableFrom <PdbMemberFunctionType>(pdbType); PdbMemberFunctionType pdbMemberFunctionType = (PdbMemberFunctionType)pdbType; returnType = pdbMemberFunctionType.ReturnType; callingConvention = pdbMemberFunctionType.CallingConvention; parameterCount = pdbMemberFunctionType.ParameterCount; pdbArguments = pdbMemberFunctionType.Arguments; CompareTypes(diaType.objectPointerType, pdbMemberFunctionType.ThisType, checkedTypes); Assert.Equal(diaType.thisAdjust, pdbMemberFunctionType.ThisPointerAdjustment); } Assert.Equal(diaType.callingConvention, (uint)callingConvention); Assert.Equal(diaType.count, parameterCount); CompareTypes(diaType.type, returnType, checkedTypes); IDiaSymbol[] diaArguments = diaType.GetChildren(SymTagEnum.FunctionArgType).ToArray(); Assert.Equal(diaArguments.Length, pdbArguments.Length); for (int i = 0; i < diaArguments.Length; i++) { CompareTypes(diaArguments[i].type, pdbArguments[i], checkedTypes); } } else { throw new NotImplementedException(); } }
/// <summary> /// Initializes a new instance of the <see cref="PdbTypeField"/> class. /// </summary> /// <param name="containerType">Type that contains this field.</param> /// <param name="dataMemberRecord">The data member record.</param> internal PdbTypeField(PdbUserDefinedType containerType, DataMemberRecord dataMemberRecord) { ContainerType = containerType; DataMemberRecord = dataMemberRecord; }
/// <summary> /// Initializes a new instance of the <see cref="PdbTypeRegularStaticField"/> class. /// </summary> /// <param name="containerType">Type that contains this field.</param> /// <param name="staticDataMemberRecord">The static data member record.</param> /// <param name="data">Data symbol for this static field.</param> internal PdbTypeRegularStaticField(PdbUserDefinedType containerType, StaticDataMemberRecord staticDataMemberRecord, DataSymbol data) : base(containerType, staticDataMemberRecord) { Data = data; }
/// <summary> /// Initializes a new instance of the <see cref="PdbTypeBitField"/> class. /// </summary> /// <param name="containerType">Type that contains this field.</param> /// <param name="dataMemberRecord">The data member record.</param> /// <param name="bitFieldRecord">The bit field record.</param> internal PdbTypeBitField(PdbUserDefinedType containerType, DataMemberRecord dataMemberRecord, BitFieldRecord bitFieldRecord) : base(containerType, dataMemberRecord) { BitFieldRecord = bitFieldRecord; }
/// <summary> /// Initializes a new instance of the <see cref="PdbTypeStaticField"/> class. /// </summary> /// <param name="containerType">Type that contains this field.</param> /// <param name="staticDataMemberRecord">The static data member record.</param> internal PdbTypeStaticField(PdbUserDefinedType containerType, StaticDataMemberRecord staticDataMemberRecord) { ContainerType = containerType; StaticDataMemberRecord = staticDataMemberRecord; }
/// <summary> /// Initializes a new instance of the <see cref="PdbTypeConstant"/> class. /// </summary> /// <param name="containerType">Type that contains this field.</param> /// <param name="staticDataMemberRecord">The static data member record.</param> /// <param name="constant">The constant symbol.</param> internal PdbTypeConstant(PdbUserDefinedType containerType, StaticDataMemberRecord staticDataMemberRecord, ConstantSymbol constant) : base(containerType, staticDataMemberRecord) { Constant = constant; }
/// <summary> /// Initializes a new instance of the <see cref="PdbTypeThreadLocalStorage"/> class. /// </summary> /// <param name="containerType">Type that contains this field.</param> /// <param name="staticDataMemberRecord">The static data member record.</param> /// <param name="threadLocalData">The thread local data.</param> internal PdbTypeThreadLocalStorage(PdbUserDefinedType containerType, StaticDataMemberRecord staticDataMemberRecord, ThreadLocalDataSymbol threadLocalData) : base(containerType, staticDataMemberRecord) { ThreadLocalData = threadLocalData; }
/// <summary> /// Initializes a new instance of the <see cref="PdbFileReader"/> class. /// </summary> /// <param name="pdbFile">Opened PDB file.</param> private PdbFileReader(PdbFile pdbFile) { PdbFile = pdbFile; typesByIndex = new DictionaryCache <TypeIndex, PdbType>(CreateType); userDefinedTypesCache = SimpleCache.CreateStruct(() => { List <PdbType> types = new List <PdbType>(); var references = PdbFile.TpiStream.References; TypeLeafKind[] allowedKinds = ClassRecord.Kinds.Concat(UnionRecord.Kinds).Concat(EnumRecord.Kinds).ToArray(); for (int i = 0; i < references.Count; i++) { if (allowedKinds.Contains(references[i].Kind)) { TypeIndex typeIndex = TypeIndex.FromArrayIndex(i); TypeRecord typeRecord = PdbFile.TpiStream[typeIndex]; PdbType pdbType = typesByIndex[typeIndex]; if (typeRecord is TagRecord tagRecord) { // Check if it is forward reference and if it has been resolved. PdbUserDefinedType pdbUserDefinedType = (PdbUserDefinedType)pdbType; if (pdbUserDefinedType.TagRecord != tagRecord) { continue; } } types.Add(pdbType); } } return((IReadOnlyList <PdbType>)types); }); globalVarablesCache = SimpleCache.CreateStruct(() => { var data = PdbFile.GlobalsStream.Data; PdbGlobalVariable[] globalVariables = new PdbGlobalVariable[data.Count]; for (int i = 0; i < data.Count; i++) { globalVariables[i] = new PdbGlobalVariable(this, data[i]); } return(globalVariables); }); publicSymbolsCache = SimpleCache.CreateStruct(() => { PdbPublicSymbol[] publicSymbols = new PdbPublicSymbol[PdbFile.PublicsStream.PublicSymbols.Count]; for (int i = 0; i < publicSymbols.Length; i++) { publicSymbols[i] = new PdbPublicSymbol(this, PdbFile.PublicsStream.PublicSymbols[i]); } return(publicSymbols); }); functionsCache = SimpleCache.CreateStruct(() => { List <PdbFunction> functions = new List <PdbFunction>(); var references = PdbFile.PdbSymbolStream?.References; var modules = PdbFile.DbiStream?.Modules; if (references != null && modules != null) { HashSet <uint>[] selectedFunctions = new HashSet <uint> [modules.Count]; for (int i = 0; i < references.Count; i++) { ProcedureSymbol procedure = null; switch (references[i].Kind) { // ProcedureSymbol case SymbolRecordKind.S_GPROC32: case SymbolRecordKind.S_LPROC32: case SymbolRecordKind.S_GPROC32_ID: case SymbolRecordKind.S_LPROC32_ID: case SymbolRecordKind.S_LPROC32_DPC: case SymbolRecordKind.S_LPROC32_DPC_ID: procedure = PdbFile.PdbSymbolStream[i] as ProcedureSymbol; break; // ProcedureReferenceSymbol case SymbolRecordKind.S_PROCREF: case SymbolRecordKind.S_LPROCREF: { ProcedureReferenceSymbol procedureReference = PdbFile.PdbSymbolStream[i] as ProcedureReferenceSymbol; int moduleIndex = procedureReference.Module - 1; if (moduleIndex >= 0 && moduleIndex < modules.Count) { var module = modules[moduleIndex]; if (selectedFunctions[moduleIndex] == null) { selectedFunctions[moduleIndex] = new HashSet <uint>(); } if (!selectedFunctions[moduleIndex].Contains(procedureReference.Offset) && module.LocalSymbolStream.TryGetSymbolRecordByOffset(procedureReference.Offset, out SymbolRecord procedureSymbol)) { procedure = procedureSymbol as ProcedureSymbol; selectedFunctions[moduleIndex].Add(procedureReference.Offset); } } } break; } if (procedure != null) { functions.Add(new PdbFunction(this, procedure)); } } } return(functions); }); }