//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Diagnostics.SymbolStore; namespace Microsoft.Cci.Pdb { internal class PdbFile { private PdbFile() // This class can't be instantiated. { } static void LoadGuidStream(BitAccess bits, out Guid doctype, out Guid language, out Guid vendor) { bits.ReadGuid(out language); bits.ReadGuid(out vendor); bits.ReadGuid(out doctype); } static Dictionary LoadNameIndex(BitAccess bits) { Dictionary result = new Dictionary(); int ver; int sig; int age; Guid guid; bits.ReadInt32(out ver); // 0..3 Version bits.ReadInt32(out sig); // 4..7 Signature bits.ReadInt32(out age); // 8..11 Age bits.ReadGuid(out guid); // 12..27 GUID if (ver != 20000404) { throw new PdbDebugException("Unsupported PDB Stream version {0}", ver); } // Read string buffer. int buf; bits.ReadInt32(out buf); // 28..31 Bytes of Strings int beg = bits.Position; int nxt = bits.Position + buf; bits.Position = nxt; // Read map index. int cnt; // n+0..3 hash size. int max; // n+4..7 maximum ni. bits.ReadInt32(out cnt); bits.ReadInt32(out max); BitSet present = new BitSet(bits); BitSet deleted = new BitSet(bits); if (!deleted.IsEmpty) { throw new PdbDebugException("Unsupported PDB deleted bitset is not empty."); } int j = 0; for (int i = 0; i < max; i++) { if (present.IsSet(i)) { int ns; int ni; bits.ReadInt32(out ns); bits.ReadInt32(out ni); string name; int saved = bits.Position; bits.Position = beg + ns; bits.ReadCString(out name); bits.Position = saved; result.Add(name, ni); j++; } } if (j != cnt) { throw new PdbDebugException("Count mismatch. ({0} != {1})", j, cnt); } return result; } static IntHashTable LoadNameStream(BitAccess bits) { IntHashTable ht = new IntHashTable(); uint sig; int ver; bits.ReadUInt32(out sig); // 0..3 Signature bits.ReadInt32(out ver); // 4..7 Version // Read (or skip) string buffer. int buf; bits.ReadInt32(out buf); // 8..11 Bytes of Strings if (sig != 0xeffeeffe || ver != 1) { throw new PdbDebugException("Unsupported Name Stream version. "+ "(sig={0:x8}, ver={1})", sig, ver); } int beg = bits.Position; int nxt = bits.Position + buf; bits.Position = nxt; // Read hash table. int siz; bits.ReadInt32(out siz); // n+0..3 Number of hash buckets. nxt = bits.Position; for (int i = 0; i < siz; i++) { int ni; string name; bits.ReadInt32(out ni); if (ni != 0) { int saved = bits.Position; bits.Position = beg + ni; bits.ReadCString(out name); bits.Position = saved; ht.Add(ni, name); } } bits.Position = nxt; return ht; } private static PdbFunction match = new PdbFunction(); private static PdbFunction FindFunction(PdbFunction[] funcs, ushort sec, uint off) { match.segment = sec; match.address = off; int item = Array.BinarySearch(funcs, match, PdbFunction.byAddress); if (item >= 0) { return funcs[item]; } return null; } static void LoadManagedLines(PdbFunction[] funcs, IntHashTable names, BitAccess bits, MsfDirectory dir, Dictionary nameIndex, PdbReader reader, uint limit) { Array.Sort(funcs, PdbFunction.byAddress); IntHashTable checks = new IntHashTable(); // Read the files first int begin = bits.Position; while (bits.Position < limit) { int sig; int siz; bits.ReadInt32(out sig); bits.ReadInt32(out siz); int place = bits.Position; int endSym = bits.Position + siz; switch ((DEBUG_S_SUBSECTION)sig) { case DEBUG_S_SUBSECTION.FILECHKSMS: while (bits.Position < endSym) { CV_FileCheckSum chk; int ni = bits.Position - place; bits.ReadUInt32(out chk.name); bits.ReadUInt8(out chk.len); bits.ReadUInt8(out chk.type); string name = (string)names[(int)chk.name]; int guidStream; Guid doctypeGuid = SymDocumentType.Text; Guid languageGuid = SymLanguageType.CSharp; Guid vendorGuid = SymLanguageVendor.Microsoft; if (nameIndex.TryGetValue("/src/files/"+name, out guidStream)) { var guidBits = new BitAccess(0x100); dir.streams[guidStream].Read(reader, guidBits); LoadGuidStream(guidBits, out doctypeGuid, out languageGuid, out vendorGuid); } PdbSource src = new PdbSource((uint)ni, name, doctypeGuid, languageGuid, vendorGuid); checks.Add(ni, src); bits.Position += chk.len; bits.Align(4); } bits.Position = endSym; break; default: bits.Position = endSym; break; } } // Read the lines next. bits.Position = begin; while (bits.Position < limit) { int sig; int siz; bits.ReadInt32(out sig); bits.ReadInt32(out siz); int endSym = bits.Position + siz; switch ((DEBUG_S_SUBSECTION)sig) { case DEBUG_S_SUBSECTION.LINES: { CV_LineSection sec; bits.ReadUInt32(out sec.off); bits.ReadUInt16(out sec.sec); bits.ReadUInt16(out sec.flags); bits.ReadUInt32(out sec.cod); PdbFunction func = FindFunction(funcs, sec.sec, sec.off); if (func == null) break; // Count the line blocks. int begSym = bits.Position; int blocks = 0; while (bits.Position < endSym) { CV_SourceFile file; bits.ReadUInt32(out file.index); bits.ReadUInt32(out file.count); bits.ReadUInt32(out file.linsiz); // Size of payload. int linsiz = (int)file.count * (8 + ((sec.flags & 1) != 0 ? 4 : 0)); bits.Position += linsiz; blocks++; } func.lines = new PdbLines[blocks]; int block = 0; bits.Position = begSym; while (bits.Position < endSym) { CV_SourceFile file; bits.ReadUInt32(out file.index); bits.ReadUInt32(out file.count); bits.ReadUInt32(out file.linsiz); // Size of payload. PdbSource src = (PdbSource)checks[(int)file.index]; PdbLines tmp = new PdbLines(src, file.count); func.lines[block++] = tmp; PdbLine[] lines = tmp.lines; int plin = bits.Position; int pcol = bits.Position + 8 * (int)file.count; for (int i = 0; i < file.count; i++) { CV_Line line; CV_Column column = new CV_Column(); bits.Position = plin + 8 * i; bits.ReadUInt32(out line.offset); bits.ReadUInt32(out line.flags); uint lineBegin = line.flags & (uint)CV_Line_Flags.linenumStart; uint delta = (line.flags & (uint)CV_Line_Flags.deltaLineEnd) >> 24; bool statement = ((line.flags & (uint)CV_Line_Flags.fStatement) == 0); if ((sec.flags & 1) != 0) { bits.Position = pcol + 4 * i; bits.ReadUInt16(out column.offColumnStart); bits.ReadUInt16(out column.offColumnEnd); } lines[i] = new PdbLine(line.offset, lineBegin, column.offColumnStart, lineBegin+delta, column.offColumnEnd); } } break; } } bits.Position = endSym; } } static void LoadFuncsFromDbiModule(BitAccess bits, DbiModuleInfo info, IntHashTable names, ArrayList funcList, bool readStrings, MsfDirectory dir, Dictionary nameIndex, PdbReader reader) { PdbFunction[] funcs = null; bits.Position = 0; int sig; bits.ReadInt32(out sig); if (sig != 4) { throw new PdbDebugException("Invalid signature. (sig={0})", sig); } bits.Position = 4; // Console.WriteLine("{0}:", info.moduleName); funcs = PdbFunction.LoadManagedFunctions(info.moduleName, bits, (uint)info.cbSyms, readStrings); if (funcs != null) { bits.Position = info.cbSyms + info.cbOldLines; LoadManagedLines(funcs, names, bits, dir, nameIndex, reader, (uint)(info.cbSyms + info.cbOldLines + info.cbLines)); for (int i = 0; i < funcs.Length; i++) { funcList.Add(funcs[i]); } } } static void LoadDbiStream(BitAccess bits, out DbiModuleInfo[] modules, out DbiDbgHdr header, bool readStrings) { DbiHeader dh = new DbiHeader(bits); header = new DbiDbgHdr(); if (dh.sig != -1 || dh.ver != 19990903) { throw new PdbException("Unsupported DBI Stream version, sig={0}, ver={1}", dh.sig, dh.ver); } // Read gpmod section. ArrayList modList = new ArrayList(); int end = bits.Position + dh.gpmodiSize; while (bits.Position < end) { DbiModuleInfo mod = new DbiModuleInfo(bits, readStrings); modList.Add(mod); } if (bits.Position != end) { throw new PdbDebugException("Error reading DBI stream, pos={0} != {1}", bits.Position, end); } if (modList.Count > 0) { modules = (DbiModuleInfo[])modList.ToArray(typeof(DbiModuleInfo)); } else { modules = null; } // Skip the Section Contribution substream. bits.Position += dh.secconSize; // Skip the Section Map substream. bits.Position += dh.secmapSize; // Skip the File Info substream. bits.Position += dh.filinfSize; // Skip the TSM substream. bits.Position += dh.tsmapSize; // Skip the EC substream. bits.Position += dh.ecinfoSize; // Read the optional header. end = bits.Position + dh.dbghdrSize; if (dh.dbghdrSize > 0) { header = new DbiDbgHdr(bits); } bits.Position = end; } internal static PdbFunction[] LoadFunctions(Stream read, bool readAllStrings) { BitAccess bits = new BitAccess(512 * 1024); return LoadFunctions(read, bits, readAllStrings); } internal static PdbFunction[] LoadFunctions(Stream read, BitAccess bits, bool readAllStrings) { PdbFileHeader head = new PdbFileHeader(read, bits); PdbReader reader = new PdbReader(read, head.pageSize); MsfDirectory dir = new MsfDirectory(reader, head, bits); DbiModuleInfo[] modules = null; DbiDbgHdr header; dir.streams[1].Read(reader, bits); Dictionary nameIndex = LoadNameIndex(bits); int nameStream; if (!nameIndex.TryGetValue("/names", out nameStream)) { throw new PdbException("No `name' stream"); } dir.streams[nameStream].Read(reader, bits); IntHashTable names = LoadNameStream(bits); dir.streams[3].Read(reader, bits); LoadDbiStream(bits, out modules, out header, readAllStrings); ArrayList funcList = new ArrayList(); if (modules != null) { for (int m = 0; m < modules.Length; m++) { if (modules[m].stream > 0) { dir.streams[modules[m].stream].Read(reader, bits); LoadFuncsFromDbiModule(bits, modules[m], names, funcList, readAllStrings, dir, nameIndex, reader); } } } PdbFunction[] funcs = (PdbFunction[])funcList.ToArray(typeof(PdbFunction)); // After reading the functions, apply the token remapping table if it exists. if (header.snTokenRidMap != 0 && header.snTokenRidMap != 0xffff) { dir.streams[header.snTokenRidMap].Read(reader, bits); uint[] ridMap = new uint[dir.streams[header.snTokenRidMap].Length / 4]; bits.ReadUInt32(ridMap); foreach (PdbFunction func in funcs) { func.token = 0x06000000 | ridMap[func.token & 0xffffff]; } } // Array.Sort(funcs, PdbFunction.byAddress); //Array.Sort(funcs, PdbFunction.byToken); return funcs; } } }