1 // 2 // Author: 3 // Jb Evain (jbevain@gmail.com) 4 // 5 // Copyright (c) 2008 - 2015 Jb Evain 6 // Copyright (c) 2008 - 2011 Novell, Inc. 7 // 8 // Licensed under the MIT/X11 license. 9 // 10 11 using System; 12 using System.Collections.Generic; 13 using System.IO; 14 using System.IO.Compression; 15 16 using Mono.Cecil.Metadata; 17 using Mono.Cecil.PE; 18 19 namespace Mono.Cecil.Cil { 20 21 public sealed class PortablePdbReaderProvider : ISymbolReaderProvider { 22 GetSymbolReader(ModuleDefinition module, string fileName)23 public ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName) 24 { 25 Mixin.CheckModule (module); 26 Mixin.CheckFileName (fileName); 27 28 var file = File.OpenRead (Mixin.GetPdbFileName (fileName)); 29 return GetSymbolReader (module, Disposable.Owned (file as Stream), file.Name); 30 } 31 GetSymbolReader(ModuleDefinition module, Stream symbolStream)32 public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream) 33 { 34 Mixin.CheckModule (module); 35 Mixin.CheckStream (symbolStream); 36 37 return GetSymbolReader (module, Disposable.NotOwned (symbolStream), symbolStream.GetFileName ()); 38 } 39 GetSymbolReader(ModuleDefinition module, Disposable<Stream> symbolStream, string fileName)40 ISymbolReader GetSymbolReader (ModuleDefinition module, Disposable<Stream> symbolStream, string fileName) 41 { 42 return new PortablePdbReader (ImageReader.ReadPortablePdb (symbolStream, fileName), module); 43 } 44 } 45 46 public sealed class PortablePdbReader : ISymbolReader { 47 48 readonly Image image; 49 readonly ModuleDefinition module; 50 readonly MetadataReader reader; 51 readonly MetadataReader debug_reader; 52 53 bool IsEmbedded { get { return reader.image == debug_reader.image; } } 54 PortablePdbReader(Image image, ModuleDefinition module)55 internal PortablePdbReader (Image image, ModuleDefinition module) 56 { 57 this.image = image; 58 this.module = module; 59 this.reader = module.reader; 60 this.debug_reader = new MetadataReader (image, module, this.reader); 61 } 62 63 #if !READ_ONLY GetWriterProvider()64 public ISymbolWriterProvider GetWriterProvider () 65 { 66 return new PortablePdbWriterProvider (); 67 } 68 #endif 69 ProcessDebugHeader(ImageDebugHeader header)70 public bool ProcessDebugHeader (ImageDebugHeader header) 71 { 72 if (image == module.Image) 73 return true; 74 75 var entry = header.GetCodeViewEntry (); 76 if (entry == null) 77 return false; 78 79 var data = entry.Data; 80 81 if (data.Length < 24) 82 return false; 83 84 var magic = ReadInt32 (data, 0); 85 if (magic != 0x53445352) 86 return false; 87 88 var buffer = new byte [16]; 89 Buffer.BlockCopy (data, 4, buffer, 0, 16); 90 91 var module_guid = new Guid (buffer); 92 93 Buffer.BlockCopy (image.PdbHeap.Id, 0, buffer, 0, 16); 94 95 var pdb_guid = new Guid (buffer); 96 97 if (module_guid != pdb_guid) 98 return false; 99 100 ReadModule (); 101 return true; 102 } 103 ReadInt32(byte [] bytes, int start)104 static int ReadInt32 (byte [] bytes, int start) 105 { 106 return (bytes [start] 107 | (bytes [start + 1] << 8) 108 | (bytes [start + 2] << 16) 109 | (bytes [start + 3] << 24)); 110 } 111 ReadModule()112 void ReadModule () 113 { 114 module.custom_infos = debug_reader.GetCustomDebugInformation (module); 115 } 116 Read(MethodDefinition method)117 public MethodDebugInformation Read (MethodDefinition method) 118 { 119 var info = new MethodDebugInformation (method); 120 ReadSequencePoints (info); 121 ReadScope (info); 122 ReadStateMachineKickOffMethod (info); 123 ReadCustomDebugInformations (info); 124 return info; 125 } 126 ReadSequencePoints(MethodDebugInformation method_info)127 void ReadSequencePoints (MethodDebugInformation method_info) 128 { 129 method_info.sequence_points = debug_reader.ReadSequencePoints (method_info.method); 130 } 131 ReadScope(MethodDebugInformation method_info)132 void ReadScope (MethodDebugInformation method_info) 133 { 134 method_info.scope = debug_reader.ReadScope (method_info.method); 135 } 136 ReadStateMachineKickOffMethod(MethodDebugInformation method_info)137 void ReadStateMachineKickOffMethod (MethodDebugInformation method_info) 138 { 139 method_info.kickoff_method = debug_reader.ReadStateMachineKickoffMethod (method_info.method); 140 } 141 ReadCustomDebugInformations(MethodDebugInformation info)142 void ReadCustomDebugInformations (MethodDebugInformation info) 143 { 144 info.method.custom_infos = debug_reader.GetCustomDebugInformation (info.method); 145 } 146 Dispose()147 public void Dispose () 148 { 149 if (IsEmbedded) 150 return; 151 152 image.Dispose (); 153 } 154 } 155 156 public sealed class EmbeddedPortablePdbReaderProvider : ISymbolReaderProvider { 157 GetSymbolReader(ModuleDefinition module, string fileName)158 public ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName) 159 { 160 Mixin.CheckModule (module); 161 Mixin.CheckFileName (fileName); 162 163 var header = module.GetDebugHeader (); 164 var entry = header.GetEmbeddedPortablePdbEntry (); 165 if (entry == null) 166 throw new InvalidOperationException (); 167 168 return new EmbeddedPortablePdbReader ( 169 (PortablePdbReader) new PortablePdbReaderProvider ().GetSymbolReader (module, GetPortablePdbStream (entry))); 170 } 171 GetPortablePdbStream(ImageDebugHeaderEntry entry)172 static Stream GetPortablePdbStream (ImageDebugHeaderEntry entry) 173 { 174 var compressed_stream = new MemoryStream (entry.Data); 175 var reader = new BinaryStreamReader (compressed_stream); 176 reader.ReadInt32 (); // signature 177 var length = reader.ReadInt32 (); 178 var decompressed_stream = new MemoryStream (length); 179 180 using (var deflate_stream = new DeflateStream (compressed_stream, CompressionMode.Decompress, leaveOpen: true)) 181 deflate_stream.CopyTo (decompressed_stream); 182 183 return decompressed_stream; 184 } 185 GetSymbolReader(ModuleDefinition module, Stream symbolStream)186 public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream) 187 { 188 throw new NotSupportedException (); 189 } 190 } 191 192 public sealed class EmbeddedPortablePdbReader : ISymbolReader 193 { 194 private readonly PortablePdbReader reader; 195 EmbeddedPortablePdbReader(PortablePdbReader reader)196 internal EmbeddedPortablePdbReader (PortablePdbReader reader) 197 { 198 if (reader == null) 199 throw new ArgumentNullException (); 200 201 this.reader = reader; 202 } 203 204 #if !READ_ONLY GetWriterProvider()205 public ISymbolWriterProvider GetWriterProvider () 206 { 207 return new EmbeddedPortablePdbWriterProvider (); 208 } 209 #endif ProcessDebugHeader(ImageDebugHeader header)210 public bool ProcessDebugHeader (ImageDebugHeader header) 211 { 212 return reader.ProcessDebugHeader (header); 213 } 214 Read(MethodDefinition method)215 public MethodDebugInformation Read (MethodDefinition method) 216 { 217 return reader.Read (method); 218 } 219 Dispose()220 public void Dispose () 221 { 222 reader.Dispose (); 223 } 224 } 225 226 227 #if !READ_ONLY 228 229 public sealed class PortablePdbWriterProvider : ISymbolWriterProvider 230 { GetSymbolWriter(ModuleDefinition module, string fileName)231 public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName) 232 { 233 Mixin.CheckModule (module); 234 Mixin.CheckFileName (fileName); 235 236 var file = File.OpenWrite (Mixin.GetPdbFileName (fileName)); 237 return GetSymbolWriter (module, Disposable.Owned (file as Stream)); 238 } 239 GetSymbolWriter(ModuleDefinition module, Stream symbolStream)240 public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream) 241 { 242 Mixin.CheckModule (module); 243 Mixin.CheckStream (symbolStream); 244 245 return GetSymbolWriter (module, Disposable.NotOwned (symbolStream)); 246 } 247 GetSymbolWriter(ModuleDefinition module, Disposable<Stream> stream)248 ISymbolWriter GetSymbolWriter (ModuleDefinition module, Disposable<Stream> stream) 249 { 250 var metadata = new MetadataBuilder (module, this); 251 var writer = ImageWriter.CreateDebugWriter (module, metadata, stream); 252 253 return new PortablePdbWriter (metadata, module, writer); 254 } 255 } 256 257 interface IMetadataSymbolWriter : ISymbolWriter { SetMetadata(MetadataBuilder metadata)258 void SetMetadata (MetadataBuilder metadata); WriteModule()259 void WriteModule (); 260 } 261 262 public sealed class PortablePdbWriter : ISymbolWriter, IMetadataSymbolWriter { 263 264 readonly MetadataBuilder pdb_metadata; 265 readonly ModuleDefinition module; 266 readonly ImageWriter writer; 267 268 MetadataBuilder module_metadata; 269 270 bool IsEmbedded { get { return writer == null; } } 271 PortablePdbWriter(MetadataBuilder pdb_metadata, ModuleDefinition module)272 internal PortablePdbWriter (MetadataBuilder pdb_metadata, ModuleDefinition module) 273 { 274 this.pdb_metadata = pdb_metadata; 275 this.module = module; 276 } 277 PortablePdbWriter(MetadataBuilder pdb_metadata, ModuleDefinition module, ImageWriter writer)278 internal PortablePdbWriter (MetadataBuilder pdb_metadata, ModuleDefinition module, ImageWriter writer) 279 : this (pdb_metadata, module) 280 { 281 this.writer = writer; 282 } 283 IMetadataSymbolWriter.SetMetadata(MetadataBuilder metadata)284 void IMetadataSymbolWriter.SetMetadata (MetadataBuilder metadata) 285 { 286 this.module_metadata = metadata; 287 288 if (module_metadata != pdb_metadata) 289 this.pdb_metadata.metadata_builder = metadata; 290 } 291 IMetadataSymbolWriter.WriteModule()292 void IMetadataSymbolWriter.WriteModule () 293 { 294 pdb_metadata.AddCustomDebugInformations (module); 295 } 296 GetReaderProvider()297 public ISymbolReaderProvider GetReaderProvider () 298 { 299 return new PortablePdbReaderProvider (); 300 } 301 GetDebugHeader()302 public ImageDebugHeader GetDebugHeader () 303 { 304 if (IsEmbedded) 305 return new ImageDebugHeader (); 306 307 var directory = new ImageDebugDirectory () { 308 MajorVersion = 256, 309 MinorVersion = 20557, 310 Type = ImageDebugType.CodeView, 311 TimeDateStamp = (int) module.timestamp, 312 }; 313 314 var buffer = new ByteBuffer (); 315 // RSDS 316 buffer.WriteUInt32 (0x53445352); 317 // Module ID 318 buffer.WriteBytes (module.Mvid.ToByteArray ()); 319 // PDB Age 320 buffer.WriteUInt32 (1); 321 // PDB Path 322 var filename = writer.BaseStream.GetFileName (); 323 if (!string.IsNullOrEmpty (filename)) 324 filename = Path.GetFileName (filename); 325 326 buffer.WriteBytes (System.Text.Encoding.UTF8.GetBytes (filename)); 327 buffer.WriteByte (0); 328 329 var data = new byte [buffer.length]; 330 Buffer.BlockCopy (buffer.buffer, 0, data, 0, buffer.length); 331 directory.SizeOfData = data.Length; 332 333 return new ImageDebugHeader (new ImageDebugHeaderEntry (directory, data)); 334 } 335 Write(MethodDebugInformation info)336 public void Write (MethodDebugInformation info) 337 { 338 CheckMethodDebugInformationTable (); 339 340 pdb_metadata.AddMethodDebugInformation (info); 341 } 342 CheckMethodDebugInformationTable()343 void CheckMethodDebugInformationTable () 344 { 345 var mdi = pdb_metadata.table_heap.GetTable<MethodDebugInformationTable> (Table.MethodDebugInformation); 346 if (mdi.length > 0) 347 return; 348 349 // The MethodDebugInformation table has the same length as the Method table 350 mdi.rows = new Row<uint, uint> [module_metadata.method_rid - 1]; 351 mdi.length = mdi.rows.Length; 352 } 353 Dispose()354 public void Dispose () 355 { 356 if (IsEmbedded) 357 return; 358 359 WritePdbFile (); 360 } 361 WritePdbFile()362 void WritePdbFile () 363 { 364 WritePdbHeap (); 365 366 WriteTableHeap (); 367 368 writer.BuildMetadataTextMap (); 369 writer.WriteMetadataHeader (); 370 writer.WriteMetadata (); 371 372 writer.Flush (); 373 writer.stream.Dispose (); 374 } 375 WritePdbHeap()376 void WritePdbHeap () 377 { 378 var pdb_heap = pdb_metadata.pdb_heap; 379 380 pdb_heap.WriteBytes (module.Mvid.ToByteArray ()); 381 pdb_heap.WriteUInt32 (module_metadata.timestamp); 382 383 pdb_heap.WriteUInt32 (module_metadata.entry_point.ToUInt32 ()); 384 385 var table_heap = module_metadata.table_heap; 386 var tables = table_heap.tables; 387 388 ulong valid = 0; 389 for (int i = 0; i < tables.Length; i++) { 390 if (tables [i] == null || tables [i].Length == 0) 391 continue; 392 393 valid |= (1UL << i); 394 } 395 396 pdb_heap.WriteUInt64 (valid); 397 398 for (int i = 0; i < tables.Length; i++) { 399 if (tables [i] == null || tables [i].Length == 0) 400 continue; 401 402 pdb_heap.WriteUInt32 ((uint) tables [i].Length); 403 } 404 } 405 WriteTableHeap()406 void WriteTableHeap () 407 { 408 pdb_metadata.table_heap.string_offsets = pdb_metadata.string_heap.WriteStrings (); 409 pdb_metadata.table_heap.ComputeTableInformations (); 410 pdb_metadata.table_heap.WriteTableHeap (); 411 } 412 } 413 414 public sealed class EmbeddedPortablePdbWriterProvider : ISymbolWriterProvider { 415 GetSymbolWriter(ModuleDefinition module, string fileName)416 public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName) 417 { 418 Mixin.CheckModule (module); 419 Mixin.CheckFileName (fileName); 420 421 var stream = new MemoryStream (); 422 var pdb_writer = (PortablePdbWriter) new PortablePdbWriterProvider ().GetSymbolWriter (module, stream); 423 return new EmbeddedPortablePdbWriter (stream, pdb_writer); 424 } 425 GetSymbolWriter(ModuleDefinition module, Stream symbolStream)426 public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream) 427 { 428 throw new NotSupportedException (); 429 } 430 } 431 432 public sealed class EmbeddedPortablePdbWriter : ISymbolWriter, IMetadataSymbolWriter { 433 434 readonly Stream stream; 435 readonly PortablePdbWriter writer; 436 EmbeddedPortablePdbWriter(Stream stream, PortablePdbWriter writer)437 internal EmbeddedPortablePdbWriter (Stream stream, PortablePdbWriter writer) 438 { 439 this.stream = stream; 440 this.writer = writer; 441 } 442 GetReaderProvider()443 public ISymbolReaderProvider GetReaderProvider () 444 { 445 return new EmbeddedPortablePdbReaderProvider (); 446 } 447 GetDebugHeader()448 public ImageDebugHeader GetDebugHeader () 449 { 450 writer.Dispose (); 451 452 var directory = new ImageDebugDirectory { 453 Type = ImageDebugType.EmbeddedPortablePdb, 454 }; 455 456 var data = new MemoryStream (); 457 458 var w = new BinaryStreamWriter (data); 459 w.WriteByte (0x4d); 460 w.WriteByte (0x50); 461 w.WriteByte (0x44); 462 w.WriteByte (0x42); 463 464 w.WriteInt32 ((int) stream.Length); 465 466 stream.Position = 0; 467 468 using (var compress_stream = new DeflateStream (data, CompressionMode.Compress, leaveOpen: true)) 469 stream.CopyTo (compress_stream); 470 471 directory.SizeOfData = (int) data.Length; 472 473 return new ImageDebugHeader (new [] { 474 writer.GetDebugHeader ().Entries [0], 475 new ImageDebugHeaderEntry (directory, data.ToArray ()) 476 }); 477 } 478 Write(MethodDebugInformation info)479 public void Write (MethodDebugInformation info) 480 { 481 writer.Write (info); 482 } 483 Dispose()484 public void Dispose () 485 { 486 } 487 IMetadataSymbolWriter.SetMetadata(MetadataBuilder metadata)488 void IMetadataSymbolWriter.SetMetadata (MetadataBuilder metadata) 489 { 490 ((IMetadataSymbolWriter) writer).SetMetadata (metadata); 491 } 492 IMetadataSymbolWriter.WriteModule()493 void IMetadataSymbolWriter.WriteModule () 494 { 495 ((IMetadataSymbolWriter) writer).WriteModule (); 496 } 497 } 498 499 #endif 500 501 static class PdbGuidMapping { 502 503 static readonly Dictionary<Guid, DocumentLanguage> guid_language = new Dictionary<Guid, DocumentLanguage> (); 504 static readonly Dictionary<DocumentLanguage, Guid> language_guid = new Dictionary<DocumentLanguage, Guid> (); 505 PdbGuidMapping()506 static PdbGuidMapping () 507 { 508 AddMapping (DocumentLanguage.C, new Guid ("63a08714-fc37-11d2-904c-00c04fa302a1")); 509 AddMapping (DocumentLanguage.Cpp, new Guid ("3a12d0b7-c26c-11d0-b442-00a0244a1dd2")); 510 AddMapping (DocumentLanguage.CSharp, new Guid ("3f5162f8-07c6-11d3-9053-00c04fa302a1")); 511 AddMapping (DocumentLanguage.Basic, new Guid ("3a12d0b8-c26c-11d0-b442-00a0244a1dd2")); 512 AddMapping (DocumentLanguage.Java, new Guid ("3a12d0b4-c26c-11d0-b442-00a0244a1dd2")); 513 AddMapping (DocumentLanguage.Cobol, new Guid ("af046cd1-d0e1-11d2-977c-00a0c9b4d50c")); 514 AddMapping (DocumentLanguage.Pascal, new Guid ("af046cd2-d0e1-11d2-977c-00a0c9b4d50c")); 515 AddMapping (DocumentLanguage.Cil, new Guid ("af046cd3-d0e1-11d2-977c-00a0c9b4d50c")); 516 AddMapping (DocumentLanguage.JScript, new Guid ("3a12d0b6-c26c-11d0-b442-00a0244a1dd2")); 517 AddMapping (DocumentLanguage.Smc, new Guid ("0d9b9f7b-6611-11d3-bd2a-0000f80849bd")); 518 AddMapping (DocumentLanguage.MCpp, new Guid ("4b35fde8-07c6-11d3-9053-00c04fa302a1")); 519 AddMapping (DocumentLanguage.FSharp, new Guid ("ab4f38c9-b6e6-43ba-be3b-58080b2ccce3")); 520 } 521 AddMapping(DocumentLanguage language, Guid guid)522 static void AddMapping (DocumentLanguage language, Guid guid) 523 { 524 guid_language.Add (guid, language); 525 language_guid.Add (language, guid); 526 } 527 528 static readonly Guid type_text = new Guid ("5a869d0b-6611-11d3-bd2a-0000f80849bd"); 529 ToType(this Guid guid)530 public static DocumentType ToType (this Guid guid) 531 { 532 if (guid == type_text) 533 return DocumentType.Text; 534 535 return DocumentType.Other; 536 } 537 ToGuid(this DocumentType type)538 public static Guid ToGuid (this DocumentType type) 539 { 540 if (type == DocumentType.Text) 541 return type_text; 542 543 return new Guid (); 544 } 545 546 static readonly Guid hash_md5 = new Guid ("406ea660-64cf-4c82-b6f0-42d48172a799"); 547 static readonly Guid hash_sha1 = new Guid ("ff1816ec-aa5e-4d10-87f7-6f4963833460"); 548 static readonly Guid hash_sha256 = new Guid ("8829d00f-11b8-4213-878b-770e8597ac16"); 549 ToHashAlgorithm(this Guid guid)550 public static DocumentHashAlgorithm ToHashAlgorithm (this Guid guid) 551 { 552 if (guid == hash_md5) 553 return DocumentHashAlgorithm.MD5; 554 555 if (guid == hash_sha1) 556 return DocumentHashAlgorithm.SHA1; 557 558 if (guid == hash_sha256) 559 return DocumentHashAlgorithm.SHA256; 560 561 return DocumentHashAlgorithm.None; 562 } 563 ToGuid(this DocumentHashAlgorithm hash_algo)564 public static Guid ToGuid (this DocumentHashAlgorithm hash_algo) 565 { 566 if (hash_algo == DocumentHashAlgorithm.MD5) 567 return hash_md5; 568 569 if (hash_algo == DocumentHashAlgorithm.SHA1) 570 return hash_sha1; 571 572 return new Guid (); 573 } 574 ToLanguage(this Guid guid)575 public static DocumentLanguage ToLanguage (this Guid guid) 576 { 577 DocumentLanguage language; 578 if (!guid_language.TryGetValue (guid, out language)) 579 return DocumentLanguage.Other; 580 581 return language; 582 } 583 ToGuid(this DocumentLanguage language)584 public static Guid ToGuid (this DocumentLanguage language) 585 { 586 Guid guid; 587 if (!language_guid.TryGetValue (language, out guid)) 588 return new Guid (); 589 590 return guid; 591 } 592 593 static readonly Guid vendor_ms = new Guid ("994b45c4-e6e9-11d2-903f-00c04fa302a1"); 594 ToVendor(this Guid guid)595 public static DocumentLanguageVendor ToVendor (this Guid guid) 596 { 597 if (guid == vendor_ms) 598 return DocumentLanguageVendor.Microsoft; 599 600 return DocumentLanguageVendor.Other; 601 } 602 ToGuid(this DocumentLanguageVendor vendor)603 public static Guid ToGuid (this DocumentLanguageVendor vendor) 604 { 605 if (vendor == DocumentLanguageVendor.Microsoft) 606 return vendor_ms; 607 608 return new Guid (); 609 } 610 } 611 } 612