private void _FindPossibleOffsetsFromDerivedClass(DbgUdtTypeInfo baseType, int curOffset, List <int> offsets) { // Breadth-first search. foreach (var bc in BaseClasses) { if (bc._DuckTypeEquals(baseType)) { int offset = curOffset - (int)bc.Offset; // relative to derived class! offsets.Add(offset); // I think I could actually break out of this loop here, but I'm too // chicken. } } foreach (var bc in BaseClasses) { int tmpOffset = curOffset; tmpOffset -= (int)bc.Offset; // relative to derived class! bc._FindPossibleOffsetsFromDerivedClass(baseType, tmpOffset, offsets); } } // end _FindPossibleOffsetsFromDerivedClass()
} // end _DuckTypeEquals() public List <int> FindPossibleOffsetsFromDerivedClass(DbgUdtTypeInfo baseType) { var offsets = new List <int>(); _FindPossibleOffsetsFromDerivedClass(baseType, 0, offsets); return(offsets); }
// Unfortunately type information in PDBs expose us to the messy world of the // linker, where types have to match up across modules by name. (In other words, // every module has its own type information for IUnknown; or, IUnknown has a // different typeId in every module.) Sometimes we need to be able to make the // link between type information across modules (i.e. // TestNativeConsoleApp!IXmlReader == XmlLite!IXmlReader) in order to jump the gap // between declared types and implementing types. We do this in the same spirit as // "Duck Typing": if it looks like a duck, sounds like a duck... private bool _DuckTypeEquals(DbgUdtTypeInfo other) { if (Size != other.Size) { return(false); } if (0 == Util.Strcmp_OI(Name, other.Name)) { // TODO: Should we add any other points of comparison? Members? Perhaps // we should add some more to help detect bugs (in the target, where, // for instance, mismatched binaries could lead to mismatched types). return(true); } foreach (var equivSet in sm_interfaceNameEquivalences) { if (equivSet.Contains(Name) && equivSet.Contains(other.Name)) { LogManager.Trace("_DuckTypeEquals: Special case: {0} is equivalent to {1}.", Name, other.Name); return(true); } } // TODO: Try stripping __abi_ off? return(false); } // end _DuckTypeEquals()
} // end _EnsureInitialized() public static bool TryFindPossibleOffsetFromDerivedClass(DbgEngDebugger debugger, ulong vtableAddr, ulong firstSlotPtr, DbgUdtTypeInfo derivedType, DbgUdtTypeInfo baseType, out int offset) { _EnsureInitialized(); foreach (var plugin in sm_dtdPlugins) { using (var logger = new LogAdapter(plugin.Name)) { if (plugin.TryFindPossibleOffsetFromDerivedClass(debugger, logger, vtableAddr, firstSlotPtr, derivedType, baseType, out offset)) { LogManager.Trace("Possible offset from derived class ({0}{1:x}) found by plugin: {2}", offset > 0 ? "0x" : "", offset, plugin.Name); return(true); } } } // end foreach( plugin ) // No plugin could figure it out. offset = 0; return(false); } // end TryFindPossibleOffsetFromDerivedClass()
} // end GetHashCode() #endregion IEquatable stuff /// <summary> /// Finds the offset for a given member/member path from the start of the /// specified type. (memberPath can contain dots, drilling into members) /// </summary> public uint FindMemberOffset(string memberPath) { uint offset = 0; string[] memberNames = memberPath.Split('.'); DbgUdtTypeInfo curType = this; for (int idx = 0; idx < memberNames.Length; idx++) { string memberName = memberNames[idx]; if (!curType.Members.HasItemNamed(memberName)) { throw new ArgumentException(Util.Sprintf("'{0}' does not have a member called '{1}'.", FullyQualifiedName, memberName), "memberPath"); } var member = curType.Members[memberName]; offset += member.Offset; bool notLastTime = idx < (memberNames.Length - 1); if (notLastTime) { if (member.DataType is DbgPointerTypeInfo) { throw new ArgumentException(Util.Sprintf("There can't be a pointer in the memberPath ('{0}').", memberName), "memberPath"); } curType = member.DataType as DbgUdtTypeInfo; if (null == curType) { throw new Exception(Util.Sprintf("Unexpected DataType: '{0}' is a {1}, not a UDT.", memberName, member.DataType.GetType().FullName)); } } // end if( not the last one ) } // end for( each memberName ) return(offset); } // end FindMemberOffset
public IReadOnlyList <DbgTemplateNode> GetTemplateNodes() { if (null == m_templateNodes) { var templateTypeNames = new List <DbgTemplateNode>(); DbgArrayTypeInfo ati = this as DbgArrayTypeInfo; if (null != ati) { templateTypeNames.Add(DbgTemplateNode.CrackTemplate(ati.ArrayElementType.Name + "[]")); // we don't want the dimensions // TODO: And maybe for array types, we'd like to include the array type /with/ dimensions, // so that, for instance, all Foo[8] could be treated specially or something. But maybe that's // stretching it. // TODO: How about co-(or is it contra-)variance? (should we get base types of the element type, like we do for pointers?) } else if (typeof(DbgUdtTypeInfo).IsAssignableFrom(GetType())) { DbgUdtTypeInfo uti = (DbgUdtTypeInfo)this; templateTypeNames.Add(uti.TemplateNode); _AddBaseClassNodesToList(templateTypeNames, uti); } else if (typeof(DbgPointerTypeInfo).IsAssignableFrom(GetType())) { DbgPointerTypeInfo pti = (DbgPointerTypeInfo)this; templateTypeNames.Add(pti.TemplateNode); _AddBaseClassPointerNodesToList(templateTypeNames, pti); } else { templateTypeNames.Add(DbgTemplateNode.CrackTemplate(Name)); } m_templateNodes = templateTypeNames.AsReadOnly(); } return(m_templateNodes); } // end GetTemplateNodes()
} // end GetTemplateNodes() private void _AddBaseClassNodesToList(List <DbgTemplateNode> list, DbgUdtTypeInfo uti) { uti.VisitAllBaseClasses((bc) => list.Add(bc.TemplateNode)); } // end _AddBaseClassNodesToList()
public bool TryFindPossibleOffsetFromDerivedClass(DbgEngDebugger debugger, ILogger logger, ulong vtableAddr, ulong firstSlotPtr, DbgUdtTypeInfo derivedType, // AKA the implementing type DbgUdtTypeInfo baseType, // AKA the declaredType out int offset) { offset = 0; List <int> possibleOffsets = derivedType.FindPossibleOffsetsFromDerivedClass(baseType); if (0 == possibleOffsets.Count) { // // Special case: if we are crossing module boundaries, but the type names // are the same, one module may have just a "stub" type definition // (vtables but no other members). In that case, we'll assume that the // offset is 0. // if (0 == Util.Strcmp_OI(derivedType.Name, baseType.Name)) { Util.Assert(derivedType.Module != baseType.Module); LogManager.Trace("Type match between modules: {0} -> {1}", baseType.Module.Name, derivedType.Module.Name); offset = 0; return(true); } LogManager.Trace("Could not find a relationship between {0} (declared) and {1} (alleged concrete/derived).", baseType.Name, derivedType.Name); // It could just be because we're looking at garbage data. return(false); } else if (1 == possibleOffsets.Count) { offset = possibleOffsets[0]; return(true); } else { LogManager.Trace("Thre are {0} possible offsets: {1}", possibleOffsets.Count, String.Join(", ", possibleOffsets)); // This can happen, for instance, in the "manual IUnknown" case. We could // have multiple positions where IUnknown shows up, and we're not sure // which one we have: // // > $t // ieframe!CTabProcessReference (size 0x48) // --> +0x000 CRefThread: VTable for IUnknown (rel. offset 0, 3 slots) // +0x004 _pcRef : Int4B* // +0x008 _idThread : UInt4B // +0x00c _fProcessReference : bool // +0x010 _cDestructs : Int4B // --> +0x014 IWaitOnThreadsBeforeExit: VTable for IUnknown (rel. offset 0, 3 slots) // +0x018 _dsaThreadsToWaitOn : CDSA<void *> // +0x020 _tLastPrune : UInt8B // +0x028 _fInDestructor : bool // +0x02c _csDSA : _RTL_CRITICAL_SECTION // // If there are multiple possibilities, some of them will point to // "adjustor thunks" (see // http://blogs.msdn.com/b/oldnewthing/archive/2004/02/06/68695.aspx). // There isn't any symbolic way to get the info, so we fall back once // again to parsing it out of a symbol name. // if (_TryDiscernOffsetFromAdjustorThunk(debugger, firstSlotPtr, out offset)) { Util.Assert(possibleOffsets.Contains(offset)); return(true); } LogManager.Trace("_TryDetectDerivedType: Multiple possible offsets; couldn't pick one, between {0} (declared) and {1} (alleged concrete/derived).", baseType.Name, derivedType.Name); return(false); } // end multiple possible offsets } // end TryFindPossibleOffsetFromDerivedClass()
private static List <LayoutItem> _BuildList(DbgUdtTypeInfo udt, bool sortByOffset) { var vtables = udt.FindVTables().ToList(); var list = new List <LayoutItem>(vtables.Count + udt.Members.Count); if (sortByOffset) { // This is NOT how windbg does things. In windbg, in the face of // unions, you'll see something like offsets 0, 4, 8, then 0, 4, 8 // again, etc.; similar to how the type is declared. // // Here, the caller has exercised the option to sort everything by // offset instead. List.Sort is unstable, but we added a stabilizing // factor in LayoutItem's IComparable implementation. This allows us // to keep things in mostly the same order as would be displayed by // windbg. int idx = 0; foreach (var vtr in vtables) { list.Add(new VTableLayoutItem(vtr, idx)); idx++; } foreach (var mem in udt.Members) { list.Add(new LayoutItem((int)mem.Offset, mem, idx)); idx++; } list.Sort(); } else { // (this should result in the same order as windbg displays) // // The members are already in the proper order. We just need to slip // the vtable entries in. I'll slip them in as if the list was sorted // by offset. (but actually... is that what windbg does? I need to // find some exotic vtable examples) int memIdx = 0; foreach (var vt in vtables) { var vtli = new VTableLayoutItem(vt, 0); while ((memIdx < udt.Members.Count) && (vt.TotalOffset > udt.Members[memIdx].Offset)) { list.Add(new LayoutItem((int)udt.Members[memIdx].Offset, udt.Members[memIdx], memIdx)); memIdx++; } list.Add(vtli); } // Finish the rest. for ( ; memIdx < udt.Members.Count; memIdx++) { list.Add(new LayoutItem((int)udt.Members[memIdx].Offset, udt.Members[memIdx], memIdx)); } } return(list); } // end _BuildList()
internal InstanceLayout(DbgUdtTypeInfo udt, bool sortByOffset) { Items = new ReadOnlyCollection <LayoutItem>(_BuildList(udt, sortByOffset)); m_udt = udt; m_isSorted = sortByOffset; }