private static void LoadFunctionsFromDBIModule( StreamInfo stream, Int32 pageSize, Int32[][] streamPages, Int32[] streamSizes, Byte[] array, IDictionary <String, Int32> streamNameIndices, IDictionary <Int32, String> nameIndex, DBIModuleInfo moduleInfo, PDBInstance instance ) { var idx = INT_SIZE; // Skip signature var thisFuncs = new List <PDBFunctionInfo>(); var module = instance.GetOrAddModule(moduleInfo.moduleName); while (idx < moduleInfo.symbolByteCount) { var blockLen = array.ReadUInt16LEFromBytes(ref idx); var blockEndIdx = idx + blockLen; #if DEBUG if (blockEndIdx % 4 != 0) { throw new PDBException("Debyyg"); } #endif switch (array.ReadUInt16LEFromBytes(ref idx)) { case SYM_GLOBAL_MANAGED_FUNC: case SYM_LOCAL_MANAGED_FUNC: Int32 addr; UInt16 seg; var func = NewPDBFunction(moduleInfo.moduleName, array, ref idx, blockEndIdx, out addr, out seg); module.Functions.Add(func); thisFuncs.Add(new PDBFunctionInfo(func, addr, seg, -1)); // Function pointer is not used during reading break; default: #if DEBUG throw new PDBException("Debyyg"); #else idx = blockEndIdx; break; #endif } } if (thisFuncs.Count > 0) { // Seek to line information idx = moduleInfo.symbolByteCount + moduleInfo.oldLinesByteCount; // Sort the functions based on their address and token in order for fast lookup thisFuncs.Sort(PDB_FUNC_ADDRESS_AND_TOKEN_BASED); // Load PDBSources and PDBLines and modify PDBFunction's lineInfo to contain the information about sources and lines. LoadSourcesAndLinesFromModuleStream(stream, pageSize, streamPages, streamSizes, array, idx, idx + moduleInfo.linesByteCount, streamNameIndices, nameIndex, instance, thisFuncs); } }
public static PDBInstance FromStream(Stream readingStream, Boolean ignoreCaseSourceFileStreamNames = true) { var stream = new StreamInfo(readingStream); // Header // Skip first 32 bytes (it's just text and magic ints) stream.stream.SeekFromCurrent(32); var array = new Byte[20]; stream.stream.ReadWholeArray(array); var idx = 0; var pageSize = array.ReadInt32LEFromBytes(ref idx); if (pageSize <= 0) { // Maybe instead just return empty PDBInstance ? throw new PDBException("Page size must be greater than zero, was: " + pageSize + "."); } array.Skip(ref idx, 8); // Skip free page map size and page count var directoryByteCount = array.ReadInt32LEFromBytes(ref idx); // Ignore last 4 bytes // Read directory page offsets var directoryPageCount = AmountOfPagesTaken(directoryByteCount, pageSize); var directoryPageOffsetPageCount = AmountOfPagesTaken( INT_SIZE * directoryPageCount, pageSize); array = stream.stream.ReadWholeArray(INT_SIZE * directoryPageOffsetPageCount); idx = 0; var directoryPageOffsets = array.ReadInt32ArrayLEFromBytes(ref idx, directoryPageOffsetPageCount); // Directory // Read directory pages array = stream.ReadPagedData(pageSize, directoryPageOffsets, directoryPageCount * INT_SIZE); idx = 0; directoryPageOffsets = array.ReadInt32ArrayLEFromBytes(ref idx, directoryPageCount); // Read directory data array = stream.ReadPagedData(pageSize, directoryPageOffsets, directoryByteCount); idx = 0; var dataStreamCount = array.ReadInt32LEFromBytes(ref idx); var dataStreamSizes = array.ReadInt32ArrayLEFromBytes(ref idx, dataStreamCount); var dataStreamPages = new Int32[dataStreamCount][]; for (var i = 0; i < dataStreamCount; ++i) { var dSize = dataStreamSizes[i]; if (dSize > 0) { dataStreamPages[i] = array.ReadInt32ArrayLEFromBytes(ref idx, AmountOfPagesTaken(dSize, pageSize)); } } // Read DBI data array = stream.ReadPagedData(pageSize, dataStreamPages[3], dataStreamSizes[3]); idx = 0; DBIHeader dbiHeader; DBIModuleInfo[] modules; DBIDebugHeader debugHeader; LoadDBIStream(array, ref idx, out dbiHeader, out modules, out debugHeader); // Make array have the size of biggest stream size to be used, so we wouldn't need to create new array each time for stream // TODO this might still be too much in some gigantic function cases... array = new Byte[dataStreamSizes.Where((siz, i) => i != 3 && i != dbiHeader.symRecStream && i != dbiHeader.psSymStream && i != dbiHeader.gsSymStream).Max()]; // Create result instance var instance = new PDBInstance(); // Read root stream stream.ReadPagedData(pageSize, dataStreamPages[1], dataStreamSizes[1], array); idx = 8; // Skip version & timestamp instance.Age = array.ReadUInt32LEFromBytes(ref idx); instance.DebugGUID = array.ReadGUIDFromBytes(ref idx); var streamNameIndices = LoadStreamIndices(array, ref idx, ignoreCaseSourceFileStreamNames); Int32 namesStreamIdx; if (!streamNameIndices.TryGetValue(NAMES_STREAM_NAME, out namesStreamIdx)) { throw new PDBException("The \"" + NAMES_STREAM_NAME + "\" stream is missing."); } else if (namesStreamIdx >= dataStreamCount) { throw new PDBException("The \"" + NAMES_STREAM_NAME + "\" stream was referencing to non-existant stream."); } // Read symbol server data, if any Int32 srcStrmIdx; if (streamNameIndices.TryGetValue(SOURCE_SERVER_STREAM_NAME, out srcStrmIdx) && srcStrmIdx < dataStreamCount) { stream.ReadPagedData(pageSize, dataStreamPages[srcStrmIdx], dataStreamSizes[srcStrmIdx], array); idx = 0; instance.SourceServer = array.ReadStringWithEncoding(ref idx, dataStreamSizes[srcStrmIdx], NAME_ENCODING); } // Read name index. stream.ReadPagedData(pageSize, dataStreamPages[namesStreamIdx], dataStreamSizes[namesStreamIdx], array); idx = 0; /*var nsSig = */ array.ReadUInt32LEFromBytes(ref idx); /*var nsVer = */ array.ReadInt32LEFromBytes(ref idx); var nameIndex = LoadNameIndex(array, ref idx); // TEMP BEGIN //stream.ReadPagedData( pageSize, dataStreamPages[7], dataStreamSizes[7], array ); //idx = 4; //var max = array.ReadInt32LEFromBytes( ref idx ); //idx = 0x58; //var srcHdrStrs = new List<String[]>(); //while ( idx < max ) //{ // var sIdx = array.ReadInt32LEFromBytes( ref idx ); // var str1 = nameIndex[sIdx]; // var max2 = array.ReadInt32LEFromBytes( ref idx ); // if ( max2 != 0x28 ) // { // } // max2 += idx - 4; // idx += 8; // if ( array.ReadInt32LEFromBytes( ref idx ) != 0x58 ) // { // } // var strs = new List<String>(); // strs.Add( str1 ); // while ( idx < max2 ) // { // var curStr = nameIndex[array.ReadInt32LEFromBytes( ref idx )]; // strs.Add( curStr ); // if ( String.Equals( curStr, strs[0] ) ) // { // if ( array.ReadInt32LEFromBytes( ref idx ) != 0x65 ) // { // } // idx = max2; // break; // } // } // srcHdrStrs.Add( new String[] { "" + sIdx + " - " + ( sIdx % 52 ) + ": " + str1 } );// strs.ToArray() ); //} //var srcHdrStr = String.Join( "\n", srcHdrStrs.Select( s => String.Join( "\n", s ) ) ); // TEMP END // Read modules. foreach (var module in modules) { if (module.stream > 0 && module.stream < dataStreamCount) { stream.ReadPagedData(pageSize, dataStreamPages[module.stream], dataStreamSizes[module.stream], array); LoadFunctionsFromDBIModule(stream, pageSize, dataStreamPages, dataStreamSizes, array, streamNameIndices, nameIndex, module, instance); } } // Apply token remapping table if exists if (debugHeader != null && debugHeader.snTokenRidMap != UInt16.MinValue && debugHeader.snTokenRidMap != UInt16.MaxValue) { var tokenRemapSize = dataStreamSizes[debugHeader.snTokenRidMap]; stream.ReadPagedData(pageSize, dataStreamPages[debugHeader.snTokenRidMap], tokenRemapSize, array); idx = 0; var tokens = array.ReadUInt32ArrayLEFromBytes(ref idx, tokenRemapSize / INT_SIZE); foreach (var function in instance.Modules.SelectMany(mod => mod.Functions)) { function.Token = METHOD_TABLE | tokens[function.Token & METHOD_TABLE_INDEX_MASK]; } } return(instance); }
private static IDictionary <Int32, PDBSource> LoadSourcesAndLinesFromModuleStream( StreamInfo stream, Int32 pageSize, Int32[][] streamPages, Int32[] streamSizes, Byte[] array, Int32 idx, Int32 max, IDictionary <String, Int32> streamNameIndices, IDictionary <Int32, String> nameIndex, PDBInstance instance, List <PDBFunctionInfo> functions ) { var sourcesLocal = new Dictionary <Int32, PDBSource>(); var lines = new List <Tuple <Int32, PDBLine> > [functions.Count]; while (idx < max) { var sym = array.ReadInt32LEFromBytes(ref idx); var size = array.ReadInt32LEFromBytes(ref idx); var startIdx = idx; var endIdx = idx + size; switch (sym) { case SYM_DEBUG_SOURCE_INFO: while (idx < endIdx) { var curSrcFileIdx = idx - startIdx; var nameIdx = array.ReadInt32LEFromBytes(ref idx); var thisLen = array.ReadByteFromBytes(ref idx); /*var kind = */ array.ReadByteFromBytes(ref idx); var name = nameIndex[nameIdx]; PDBSource pdbSource; if (!instance.TryGetSource(name, out pdbSource)) { pdbSource = new PDBSource(name); Int32 sourceStreamIdx; if (streamNameIndices.TryGetValue(SOURCE_FILE_PREFIX + name, out sourceStreamIdx)) { var sourceBytes = stream.ReadPagedData(pageSize, streamPages[sourceStreamIdx], streamSizes[sourceStreamIdx]); var tmpIdx = 0; pdbSource.Language = sourceBytes.ReadGUIDFromBytes(ref tmpIdx); pdbSource.Vendor = sourceBytes.ReadGUIDFromBytes(ref tmpIdx); pdbSource.DocumentType = sourceBytes.ReadGUIDFromBytes(ref tmpIdx); pdbSource.HashAlgorithm = sourceBytes.ReadGUIDFromBytes(ref tmpIdx); pdbSource.Hash = sourceBytes.CreateAndBlockCopyTo(ref tmpIdx, sourceBytes.Length - tmpIdx); } instance.AddSource(pdbSource); } ; sourcesLocal.Add(curSrcFileIdx, pdbSource); #if DEBUG if (thisLen != 0) { throw new PDBException("Debyyg"); } #endif idx += thisLen; Align4(ref idx); } break; case SYM_DEBUG_LINE_INFO: var addr = array.ReadInt32LEFromBytes(ref idx); var section = array.ReadUInt16LEFromBytes(ref idx); var flags = array.ReadUInt16LEFromBytes(ref idx); /*var cod = */ array.ReadUInt32LEFromBytes(ref idx); var funcIdx = functions.BinarySearchDeferredEqualityDetection(new PDBFunctionInfo(null, addr, section, -1), PDB_FUNC_ADDRESS_AND_TOKEN_BASED); if (funcIdx >= 0) { // Skip the functions that already have lines while (funcIdx < lines.Length && lines[funcIdx] != null && functions[funcIdx].segment == section && functions[funcIdx].address == addr) { ++funcIdx; } if (funcIdx < lines.Length && functions[funcIdx].segment == section && functions[funcIdx].address == addr) { // We found the correct function index var thisLines = new List <Tuple <Int32, PDBLine> >(); // Read line data while (idx < endIdx) { var srcIdx = array.ReadInt32LEFromBytes(ref idx); var lineCount = array.ReadInt32LEFromBytes(ref idx); // Skip size information idx += INT_SIZE; // Save line and column start indices var lineStartIdx = idx; var columnStartIdx = idx + 8 * lineCount; // Each line is 2 integers // Iterate each line for (var i = 0; i < lineCount; ++i) { // Reset index after possible column read idx = lineStartIdx + LINE_MULTIPLIER * i; var offset = array.ReadInt32LEFromBytes(ref idx); var line = new PDBLine(offset); var lineFlags = array.ReadUInt32LEFromBytes(ref idx); line.LineStart = (Int32)(lineFlags & 0x00ffffffu); // Lower 3 bytes are start line of statement/expression line.LineEnd = line.LineStart + (Int32)((lineFlags & 0x7f000000u) >> 24); // High seven bits is delta of line line.IsStatement = (lineFlags & 0x80000000u) == 0; // Highest bit is whether the line is statement if ((flags & 1) != 0) { // Column info present idx = columnStartIdx + COLUMN_MULTIPLIER * i; // Each column info is two shorts line.ColumnStart = array.ReadUInt16LEFromBytes(ref idx); line.ColumnEnd = array.ReadUInt16LEFromBytes(ref idx); } thisLines.Add(Tuple.Create(srcIdx, line)); } } lines[funcIdx] = thisLines; } #if DEBUG else { throw new PDBException("Debyyg"); } #endif } #if DEBUG else { throw new PDBException("Debyyg"); } #endif break; default: #if DEBUG throw new PDBException("Debyyg"); #else break; #endif } idx = endIdx; } // Postprocess line infos for (var i = 0; i < lines.Length; ++i) { var lineList = lines[i]; if (lineList != null) { foreach (var line in lineList) { functions[i].function.Lines.GetOrAdd_NotThreadSafe(sourcesLocal[line.Item1].Name, ni => new List <PDBLine>()) .Add(line.Item2); } } } return(sourcesLocal); }