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