1 //----------------------------------------------------------------------------- 2 // 3 // Copyright (c) Microsoft. All rights reserved. 4 // This code is licensed under the Microsoft Public License. 5 // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 6 // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY 7 // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR 8 // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. 9 // 10 //----------------------------------------------------------------------------- 11 using System; 12 13 using System.Collections.Generic; 14 using System.IO; 15 using Mono.Cecil.Cil; 16 17 namespace Microsoft.Cci.Pdb { 18 internal class PdbFile { PdbFile()19 private PdbFile() // This class can't be instantiated. 20 { 21 } 22 LoadGuidStream(BitAccess bits, out Guid doctype, out Guid language, out Guid vendor)23 static void LoadGuidStream(BitAccess bits, out Guid doctype, out Guid language, out Guid vendor) { 24 bits.ReadGuid(out language); 25 bits.ReadGuid(out vendor); 26 bits.ReadGuid(out doctype); 27 } 28 LoadNameIndex(BitAccess bits, out int age, out Guid guid)29 static Dictionary<string, int> LoadNameIndex(BitAccess bits, out int age, out Guid guid) { 30 Dictionary<string, int> result = new Dictionary<string, int>(); 31 int ver; 32 int sig; 33 bits.ReadInt32(out ver); // 0..3 Version 34 bits.ReadInt32(out sig); // 4..7 Signature 35 bits.ReadInt32(out age); // 8..11 Age 36 bits.ReadGuid(out guid); // 12..27 GUID 37 38 //if (ver != 20000404) { 39 // throw new PdbDebugException("Unsupported PDB Stream version {0}", ver); 40 //} 41 42 // Read string buffer. 43 int buf; 44 bits.ReadInt32(out buf); // 28..31 Bytes of Strings 45 46 int beg = bits.Position; 47 int nxt = bits.Position + buf; 48 49 bits.Position = nxt; 50 51 // Read map index. 52 int cnt; // n+0..3 hash size. 53 int max; // n+4..7 maximum ni. 54 55 bits.ReadInt32(out cnt); 56 bits.ReadInt32(out max); 57 58 BitSet present = new BitSet(bits); 59 BitSet deleted = new BitSet(bits); 60 if (!deleted.IsEmpty) { 61 throw new PdbDebugException("Unsupported PDB deleted bitset is not empty."); 62 } 63 64 int j = 0; 65 for (int i = 0; i < max; i++) { 66 if (present.IsSet(i)) { 67 int ns; 68 int ni; 69 bits.ReadInt32(out ns); 70 bits.ReadInt32(out ni); 71 72 string name; 73 int saved = bits.Position; 74 bits.Position = beg + ns; 75 bits.ReadCString(out name); 76 bits.Position = saved; 77 78 result.Add(name.ToUpperInvariant(), ni); 79 j++; 80 } 81 } 82 if (j != cnt) { 83 throw new PdbDebugException("Count mismatch. ({0} != {1})", j, cnt); 84 } 85 return result; 86 } 87 LoadNameStream(BitAccess bits)88 static IntHashTable LoadNameStream(BitAccess bits) { 89 IntHashTable ht = new IntHashTable(); 90 91 uint sig; 92 int ver; 93 bits.ReadUInt32(out sig); // 0..3 Signature 94 bits.ReadInt32(out ver); // 4..7 Version 95 96 // Read (or skip) string buffer. 97 int buf; 98 bits.ReadInt32(out buf); // 8..11 Bytes of Strings 99 100 if (sig != 0xeffeeffe || ver != 1) { 101 throw new PdbDebugException("Unsupported Name Stream version. "+ 102 "(sig={0:x8}, ver={1})", 103 sig, ver); 104 } 105 int beg = bits.Position; 106 int nxt = bits.Position + buf; 107 bits.Position = nxt; 108 109 // Read hash table. 110 int siz; 111 bits.ReadInt32(out siz); // n+0..3 Number of hash buckets. 112 nxt = bits.Position; 113 114 for (int i = 0; i < siz; i++) { 115 int ni; 116 string name; 117 118 bits.ReadInt32(out ni); 119 120 if (ni != 0) { 121 int saved = bits.Position; 122 bits.Position = beg + ni; 123 bits.ReadCString(out name); 124 bits.Position = saved; 125 126 ht.Add(ni, name); 127 } 128 } 129 bits.Position = nxt; 130 131 return ht; 132 } 133 FindFunction(PdbFunction[] funcs, ushort sec, uint off)134 private static int FindFunction(PdbFunction[] funcs, ushort sec, uint off) { 135 var match = new PdbFunction { 136 segment = sec, 137 address = off, 138 }; 139 140 return Array.BinarySearch(funcs, match, PdbFunction.byAddress); 141 } 142 LoadManagedLines(PdbFunction[] funcs, IntHashTable names, BitAccess bits, MsfDirectory dir, Dictionary<string, int> nameIndex, PdbReader reader, uint limit)143 static void LoadManagedLines(PdbFunction[] funcs, 144 IntHashTable names, 145 BitAccess bits, 146 MsfDirectory dir, 147 Dictionary<string, int> nameIndex, 148 PdbReader reader, 149 uint limit) { 150 Array.Sort(funcs, PdbFunction.byAddressAndToken); 151 152 int begin = bits.Position; 153 IntHashTable checks = ReadSourceFileInfo(bits, limit, names, dir, nameIndex, reader); 154 155 // Read the lines next. 156 bits.Position = begin; 157 while (bits.Position < limit) { 158 int sig; 159 int siz; 160 bits.ReadInt32(out sig); 161 bits.ReadInt32(out siz); 162 int endSym = bits.Position + siz; 163 164 switch ((DEBUG_S_SUBSECTION)sig) { 165 case DEBUG_S_SUBSECTION.LINES: { 166 CV_LineSection sec; 167 168 bits.ReadUInt32(out sec.off); 169 bits.ReadUInt16(out sec.sec); 170 bits.ReadUInt16(out sec.flags); 171 bits.ReadUInt32(out sec.cod); 172 int funcIndex = FindFunction(funcs, sec.sec, sec.off); 173 if (funcIndex < 0) break; 174 var func = funcs[funcIndex]; 175 if (func.lines == null) { 176 while (funcIndex > 0) { 177 var f = funcs[funcIndex-1]; 178 if (f.lines != null || f.segment != sec.sec || f.address != sec.off) break; 179 func = f; 180 funcIndex--; 181 } 182 } else { 183 while (funcIndex < funcs.Length-1 && func.lines != null) { 184 var f = funcs[funcIndex+1]; 185 if (f.segment != sec.sec || f.address != sec.off) break; 186 func = f; 187 funcIndex++; 188 } 189 } 190 if (func.lines != null) break; 191 192 // Count the line blocks. 193 int begSym = bits.Position; 194 int blocks = 0; 195 while (bits.Position < endSym) { 196 CV_SourceFile file; 197 bits.ReadUInt32(out file.index); 198 bits.ReadUInt32(out file.count); 199 bits.ReadUInt32(out file.linsiz); // Size of payload. 200 int linsiz = (int)file.count * (8 + ((sec.flags & 1) != 0 ? 4 : 0)); 201 bits.Position += linsiz; 202 blocks++; 203 } 204 205 func.lines = new PdbLines[blocks]; 206 int block = 0; 207 208 bits.Position = begSym; 209 while (bits.Position < endSym) { 210 CV_SourceFile file; 211 bits.ReadUInt32(out file.index); 212 bits.ReadUInt32(out file.count); 213 bits.ReadUInt32(out file.linsiz); // Size of payload. 214 215 PdbSource src = (PdbSource)checks[(int)file.index]; 216 PdbLines tmp = new PdbLines(src, file.count); 217 func.lines[block++] = tmp; 218 PdbLine[] lines = tmp.lines; 219 220 int plin = bits.Position; 221 int pcol = bits.Position + 8 * (int)file.count; 222 223 for (int i = 0; i < file.count; i++) { 224 CV_Line line; 225 CV_Column column = new CV_Column(); 226 227 bits.Position = plin + 8 * i; 228 bits.ReadUInt32(out line.offset); 229 bits.ReadUInt32(out line.flags); 230 231 uint lineBegin = line.flags & (uint)CV_Line_Flags.linenumStart; 232 uint delta = (line.flags & (uint)CV_Line_Flags.deltaLineEnd) >> 24; 233 //bool statement = ((line.flags & (uint)CV_Line_Flags.fStatement) == 0); 234 if ((sec.flags & 1) != 0) { 235 bits.Position = pcol + 4 * i; 236 bits.ReadUInt16(out column.offColumnStart); 237 bits.ReadUInt16(out column.offColumnEnd); 238 } 239 240 lines[i] = new PdbLine(line.offset, 241 lineBegin, 242 column.offColumnStart, 243 lineBegin+delta, 244 column.offColumnEnd); 245 } 246 } 247 break; 248 } 249 } 250 bits.Position = endSym; 251 } 252 } 253 LoadFuncsFromDbiModule(BitAccess bits, DbiModuleInfo info, IntHashTable names, List<PdbFunction> funcList, bool readStrings, MsfDirectory dir, Dictionary<string, int> nameIndex, PdbReader reader)254 static void LoadFuncsFromDbiModule(BitAccess bits, 255 DbiModuleInfo info, 256 IntHashTable names, 257 List<PdbFunction> funcList, 258 bool readStrings, 259 MsfDirectory dir, 260 Dictionary<string, int> nameIndex, 261 PdbReader reader) { 262 PdbFunction[] funcs = null; 263 264 bits.Position = 0; 265 int sig; 266 bits.ReadInt32(out sig); 267 if (sig != 4) { 268 throw new PdbDebugException("Invalid signature. (sig={0})", sig); 269 } 270 271 bits.Position = 4; 272 // Console.WriteLine("{0}:", info.moduleName); 273 funcs = PdbFunction.LoadManagedFunctions(/*info.moduleName,*/ 274 bits, (uint)info.cbSyms, 275 readStrings); 276 if (funcs != null) { 277 bits.Position = info.cbSyms + info.cbOldLines; 278 LoadManagedLines(funcs, names, bits, dir, nameIndex, reader, 279 (uint)(info.cbSyms + info.cbOldLines + info.cbLines)); 280 281 for (int i = 0; i < funcs.Length; i++) { 282 funcList.Add(funcs[i]); 283 } 284 } 285 } 286 LoadDbiStream(BitAccess bits, out DbiModuleInfo[] modules, out DbiDbgHdr header, bool readStrings)287 static void LoadDbiStream(BitAccess bits, 288 out DbiModuleInfo[] modules, 289 out DbiDbgHdr header, 290 bool readStrings) { 291 DbiHeader dh = new DbiHeader(bits); 292 header = new DbiDbgHdr(); 293 294 //if (dh.sig != -1 || dh.ver != 19990903) { 295 // throw new PdbException("Unsupported DBI Stream version, sig={0}, ver={1}", 296 // dh.sig, dh.ver); 297 //} 298 299 // Read gpmod section. 300 List<DbiModuleInfo> modList = new List<DbiModuleInfo>(); 301 int end = bits.Position + dh.gpmodiSize; 302 while (bits.Position < end) { 303 DbiModuleInfo mod = new DbiModuleInfo(bits, readStrings); 304 modList.Add(mod); 305 } 306 if (bits.Position != end) { 307 throw new PdbDebugException("Error reading DBI stream, pos={0} != {1}", 308 bits.Position, end); 309 } 310 311 if (modList.Count > 0) { 312 modules = modList.ToArray(); 313 } else { 314 modules = null; 315 } 316 317 // Skip the Section Contribution substream. 318 bits.Position += dh.secconSize; 319 320 // Skip the Section Map substream. 321 bits.Position += dh.secmapSize; 322 323 // Skip the File Info substream. 324 bits.Position += dh.filinfSize; 325 326 // Skip the TSM substream. 327 bits.Position += dh.tsmapSize; 328 329 // Skip the EC substream. 330 bits.Position += dh.ecinfoSize; 331 332 // Read the optional header. 333 end = bits.Position + dh.dbghdrSize; 334 if (dh.dbghdrSize > 0) { 335 header = new DbiDbgHdr(bits); 336 } 337 bits.Position = end; 338 } 339 LoadFunctions(Stream read, out Dictionary<uint, PdbTokenLine> tokenToSourceMapping, out string sourceServerData, out int age, out Guid guid)340 internal static PdbFunction[] LoadFunctions(Stream read, out Dictionary<uint, PdbTokenLine> tokenToSourceMapping, out string sourceServerData, out int age, out Guid guid) { 341 tokenToSourceMapping = new Dictionary<uint, PdbTokenLine>(); 342 BitAccess bits = new BitAccess(512 * 1024); 343 PdbFileHeader head = new PdbFileHeader(read, bits); 344 PdbReader reader = new PdbReader(read, head.pageSize); 345 MsfDirectory dir = new MsfDirectory(reader, head, bits); 346 DbiModuleInfo[] modules = null; 347 DbiDbgHdr header; 348 349 dir.streams[1].Read(reader, bits); 350 Dictionary<string, int> nameIndex = LoadNameIndex(bits, out age, out guid); 351 int nameStream; 352 if (!nameIndex.TryGetValue("/NAMES", out nameStream)) { 353 throw new PdbException("No `name' stream"); 354 } 355 dir.streams[nameStream].Read(reader, bits); 356 IntHashTable names = LoadNameStream(bits); 357 358 int srcsrvStream; 359 if (!nameIndex.TryGetValue("SRCSRV", out srcsrvStream)) 360 sourceServerData = string.Empty; 361 else { 362 DataStream dataStream = dir.streams[srcsrvStream]; 363 byte[] bytes = new byte[dataStream.contentSize]; 364 dataStream.Read(reader, bits); 365 sourceServerData = bits.ReadBString(bytes.Length); 366 } 367 368 dir.streams[3].Read(reader, bits); 369 LoadDbiStream(bits, out modules, out header, true); 370 371 List<PdbFunction> funcList = new List<PdbFunction>(); 372 373 if (modules != null) { 374 for (int m = 0; m < modules.Length; m++) { 375 var module = modules[m]; 376 if (module.stream > 0) { 377 dir.streams[module.stream].Read(reader, bits); 378 if (module.moduleName == "TokenSourceLineInfo") { 379 LoadTokenToSourceInfo(bits, module, names, dir, nameIndex, reader, tokenToSourceMapping); 380 continue; 381 } 382 LoadFuncsFromDbiModule(bits, module, names, funcList, true, dir, nameIndex, reader); 383 } 384 } 385 } 386 387 PdbFunction[] funcs = funcList.ToArray(); 388 389 // After reading the functions, apply the token remapping table if it exists. 390 if (header.snTokenRidMap != 0 && header.snTokenRidMap != 0xffff) { 391 dir.streams[header.snTokenRidMap].Read(reader, bits); 392 uint[] ridMap = new uint[dir.streams[header.snTokenRidMap].Length / 4]; 393 bits.ReadUInt32(ridMap); 394 395 foreach (PdbFunction func in funcs) { 396 func.token = 0x06000000 | ridMap[func.token & 0xffffff]; 397 } 398 } 399 400 // 401 Array.Sort(funcs, PdbFunction.byAddressAndToken); 402 //Array.Sort(funcs, PdbFunction.byToken); 403 return funcs; 404 } 405 LoadTokenToSourceInfo(BitAccess bits, DbiModuleInfo module, IntHashTable names, MsfDirectory dir, Dictionary<string, int> nameIndex, PdbReader reader, Dictionary<uint, PdbTokenLine> tokenToSourceMapping)406 private static void LoadTokenToSourceInfo(BitAccess bits, DbiModuleInfo module, IntHashTable names, MsfDirectory dir, 407 Dictionary<string, int> nameIndex, PdbReader reader, Dictionary<uint, PdbTokenLine> tokenToSourceMapping) { 408 bits.Position = 0; 409 int sig; 410 bits.ReadInt32(out sig); 411 if (sig != 4) { 412 throw new PdbDebugException("Invalid signature. (sig={0})", sig); 413 } 414 415 bits.Position = 4; 416 417 while (bits.Position < module.cbSyms) { 418 ushort siz; 419 ushort rec; 420 421 bits.ReadUInt16(out siz); 422 int star = bits.Position; 423 int stop = bits.Position + siz; 424 bits.Position = star; 425 bits.ReadUInt16(out rec); 426 427 switch ((SYM)rec) { 428 case SYM.S_OEM: 429 OemSymbol oem; 430 431 bits.ReadGuid(out oem.idOem); 432 bits.ReadUInt32(out oem.typind); 433 // internal byte[] rgl; // user data, force 4-byte alignment 434 435 if (oem.idOem == PdbFunction.msilMetaData) { 436 string name = bits.ReadString(); 437 if (name == "TSLI") { 438 uint token; 439 uint file_id; 440 uint line; 441 uint column; 442 uint endLine; 443 uint endColumn; 444 bits.ReadUInt32(out token); 445 bits.ReadUInt32(out file_id); 446 bits.ReadUInt32(out line); 447 bits.ReadUInt32(out column); 448 bits.ReadUInt32(out endLine); 449 bits.ReadUInt32(out endColumn); 450 PdbTokenLine tokenLine; 451 if (!tokenToSourceMapping.TryGetValue(token, out tokenLine)) 452 tokenToSourceMapping.Add(token, new PdbTokenLine(token, file_id, line, column, endLine, endColumn)); 453 else { 454 while (tokenLine.nextLine != null) tokenLine = tokenLine.nextLine; 455 tokenLine.nextLine = new PdbTokenLine(token, file_id, line, column, endLine, endColumn); 456 } 457 } 458 bits.Position = stop; 459 break; 460 } else { 461 throw new PdbDebugException("OEM section: guid={0} ti={1}", 462 oem.idOem, oem.typind); 463 // bits.Position = stop; 464 } 465 466 case SYM.S_END: 467 bits.Position = stop; 468 break; 469 470 default: 471 //Console.WriteLine("{0,6}: {1:x2} {2}", 472 // bits.Position, rec, (SYM)rec); 473 bits.Position = stop; 474 break; 475 } 476 } 477 478 bits.Position = module.cbSyms + module.cbOldLines; 479 int limit = module.cbSyms + module.cbOldLines + module.cbLines; 480 IntHashTable sourceFiles = ReadSourceFileInfo(bits, (uint)limit, names, dir, nameIndex, reader); 481 foreach (var tokenLine in tokenToSourceMapping.Values) { 482 tokenLine.sourceFile = (PdbSource)sourceFiles[(int)tokenLine.file_id]; 483 } 484 485 } 486 ReadSourceFileInfo(BitAccess bits, uint limit, IntHashTable names, MsfDirectory dir, Dictionary<string, int> nameIndex, PdbReader reader)487 private static IntHashTable ReadSourceFileInfo(BitAccess bits, uint limit, IntHashTable names, MsfDirectory dir, 488 Dictionary<string, int> nameIndex, PdbReader reader) { 489 IntHashTable checks = new IntHashTable(); 490 491 int begin = bits.Position; 492 while (bits.Position < limit) { 493 int sig; 494 int siz; 495 bits.ReadInt32(out sig); 496 bits.ReadInt32(out siz); 497 int place = bits.Position; 498 int endSym = bits.Position + siz; 499 500 switch ((DEBUG_S_SUBSECTION)sig) { 501 case DEBUG_S_SUBSECTION.FILECHKSMS: 502 while (bits.Position < endSym) { 503 CV_FileCheckSum chk; 504 505 int ni = bits.Position - place; 506 bits.ReadUInt32(out chk.name); 507 bits.ReadUInt8(out chk.len); 508 bits.ReadUInt8(out chk.type); 509 510 string name = (string)names[(int)chk.name]; 511 int guidStream; 512 Guid doctypeGuid = DocumentType.Text.ToGuid(); 513 Guid languageGuid = Guid.Empty; 514 Guid vendorGuid = Guid.Empty; 515 if (nameIndex.TryGetValue("/SRC/FILES/"+name.ToUpperInvariant(), out guidStream)) { 516 var guidBits = new BitAccess(0x100); 517 dir.streams[guidStream].Read(reader, guidBits); 518 LoadGuidStream(guidBits, out doctypeGuid, out languageGuid, out vendorGuid); 519 } 520 521 PdbSource src = new PdbSource(/*(uint)ni,*/ name, doctypeGuid, languageGuid, vendorGuid); 522 checks.Add(ni, src); 523 bits.Position += chk.len; 524 bits.Align(4); 525 } 526 bits.Position = endSym; 527 break; 528 529 default: 530 bits.Position = endSym; 531 break; 532 } 533 } 534 return checks; 535 } 536 } 537 } 538