1212using System . Collections . Generic ;
1313using System . IO ;
1414using System . IO . Compression ;
15-
15+ using System . Security . Cryptography ;
1616using Mono . Cecil . Metadata ;
1717using Mono . Cecil . PE ;
1818
@@ -39,7 +39,7 @@ public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStre
3939
4040 ISymbolReader GetSymbolReader ( ModuleDefinition module , Disposable < Stream > symbolStream , string fileName )
4141 {
42- return new PortablePdbReader ( ImageReader . ReadPortablePdb ( symbolStream , fileName ) , module ) ;
42+ return new PortablePdbReader ( ImageReader . ReadPortablePdb ( symbolStream , fileName , out _ ) , module ) ;
4343 }
4444 }
4545
@@ -234,24 +234,27 @@ public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName)
234234 Mixin . CheckModule ( module ) ;
235235 Mixin . CheckFileName ( fileName ) ;
236236
237- var file = File . OpenWrite ( Mixin . GetPdbFileName ( fileName ) ) ;
238- return GetSymbolWriter ( module , Disposable . Owned ( file as Stream ) ) ;
237+ var file = File . Open ( Mixin . GetPdbFileName ( fileName ) , FileMode . OpenOrCreate , FileAccess . ReadWrite ) ;
238+ return GetSymbolWriter ( module , Disposable . Owned ( file as Stream ) , Disposable . NotOwned ( ( Stream ) null ) ) ;
239239 }
240240
241241 public ISymbolWriter GetSymbolWriter ( ModuleDefinition module , Stream symbolStream )
242242 {
243243 Mixin . CheckModule ( module ) ;
244244 Mixin . CheckStream ( symbolStream ) ;
245245
246- return GetSymbolWriter ( module , Disposable . NotOwned ( symbolStream ) ) ;
246+ // In order to compute the PDB checksum, the stream we're writing to needs to be able to
247+ // seek and read as well. We can't assume this about a stream provided by the user.
248+ // So in this case, create a memory stream to cache the PDB.
249+ return GetSymbolWriter ( module , Disposable . Owned ( new MemoryStream ( ) as Stream ) , Disposable . NotOwned ( symbolStream ) ) ;
247250 }
248251
249- ISymbolWriter GetSymbolWriter ( ModuleDefinition module , Disposable < Stream > stream )
252+ ISymbolWriter GetSymbolWriter ( ModuleDefinition module , Disposable < Stream > stream , Disposable < Stream > final_stream )
250253 {
251254 var metadata = new MetadataBuilder ( module , this ) ;
252255 var writer = ImageWriter . CreateDebugWriter ( module , metadata , stream ) ;
253256
254- return new PortablePdbWriter ( metadata , module , writer ) ;
257+ return new PortablePdbWriter ( metadata , module , writer , final_stream ) ;
255258 }
256259 }
257260
@@ -260,9 +263,14 @@ public sealed class PortablePdbWriter : ISymbolWriter {
260263 readonly MetadataBuilder pdb_metadata ;
261264 readonly ModuleDefinition module ;
262265 readonly ImageWriter writer ;
266+ readonly Disposable < Stream > final_stream ;
263267
264268 MetadataBuilder module_metadata ;
265269
270+ internal byte [ ] pdb_checksum ;
271+ internal Guid pdb_id_guid ;
272+ internal uint pdb_id_stamp ;
273+
266274 bool IsEmbedded { get { return writer == null ; } }
267275
268276 internal PortablePdbWriter ( MetadataBuilder pdb_metadata , ModuleDefinition module )
@@ -278,56 +286,100 @@ internal PortablePdbWriter (MetadataBuilder pdb_metadata, ModuleDefinition modul
278286 pdb_metadata . AddCustomDebugInformations ( module ) ;
279287 }
280288
281- internal PortablePdbWriter ( MetadataBuilder pdb_metadata , ModuleDefinition module , ImageWriter writer )
289+ internal PortablePdbWriter ( MetadataBuilder pdb_metadata , ModuleDefinition module , ImageWriter writer , Disposable < Stream > final_stream )
282290 : this ( pdb_metadata , module )
283291 {
284292 this . writer = writer ;
293+ this . final_stream = final_stream ;
285294 }
286295
287296 public ISymbolReaderProvider GetReaderProvider ( )
288297 {
289298 return new PortablePdbReaderProvider ( ) ;
290299 }
291300
301+ public void Write ( MethodDebugInformation info )
302+ {
303+ CheckMethodDebugInformationTable ( ) ;
304+
305+ pdb_metadata . AddMethodDebugInformation ( info ) ;
306+ }
307+
308+ public void Write ( )
309+ {
310+ if ( IsEmbedded )
311+ return ;
312+
313+ WritePdbFile ( ) ;
314+
315+ if ( final_stream . value != null ) {
316+ writer . BaseStream . Seek ( 0 , SeekOrigin . Begin ) ;
317+ var buffer = new byte [ 8192 ] ;
318+ CryptoService . CopyStreamChunk ( writer . BaseStream , final_stream . value , buffer , ( int ) writer . BaseStream . Length ) ;
319+ }
320+ }
321+
292322 public ImageDebugHeader GetDebugHeader ( )
293323 {
294324 if ( IsEmbedded )
295325 return new ImageDebugHeader ( ) ;
296326
297- var directory = new ImageDebugDirectory ( ) {
298- MajorVersion = 256 ,
299- MinorVersion = 20557 ,
300- Type = ImageDebugType . CodeView ,
301- TimeDateStamp = ( int ) module . timestamp ,
302- } ;
303-
304- var buffer = new ByteBuffer ( ) ;
305- // RSDS
306- buffer . WriteUInt32 ( 0x53445352 ) ;
307- // Module ID
308- buffer . WriteBytes ( module . Mvid . ToByteArray ( ) ) ;
309- // PDB Age
310- buffer . WriteUInt32 ( 1 ) ;
311- // PDB Path
312- var fileName = writer . BaseStream . GetFileName ( ) ;
313- if ( string . IsNullOrEmpty ( fileName ) ) {
314- fileName = module . Assembly . Name . Name + ".pdb" ;
327+ ImageDebugHeaderEntry codeViewEntry ;
328+ {
329+ var codeViewDirectory = new ImageDebugDirectory ( ) {
330+ MajorVersion = 256 ,
331+ MinorVersion = 20557 ,
332+ Type = ImageDebugType . CodeView ,
333+ TimeDateStamp = ( int ) pdb_id_stamp ,
334+ } ;
335+
336+ var buffer = new ByteBuffer ( ) ;
337+ // RSDS
338+ buffer . WriteUInt32 ( 0x53445352 ) ;
339+ // Module ID
340+ buffer . WriteBytes ( pdb_id_guid . ToByteArray ( ) ) ;
341+ // PDB Age
342+ buffer . WriteUInt32 ( 1 ) ;
343+ // PDB Path
344+ var fileName = writer . BaseStream . GetFileName ( ) ;
345+ if ( string . IsNullOrEmpty ( fileName ) ) {
346+ fileName = module . Assembly . Name . Name + ".pdb" ;
347+ }
348+ buffer . WriteBytes ( System . Text . Encoding . UTF8 . GetBytes ( fileName ) ) ;
349+ buffer . WriteByte ( 0 ) ;
350+
351+ var data = new byte [ buffer . length ] ;
352+ Buffer . BlockCopy ( buffer . buffer , 0 , data , 0 , buffer . length ) ;
353+ codeViewDirectory . SizeOfData = data . Length ;
354+
355+ codeViewEntry = new ImageDebugHeaderEntry ( codeViewDirectory , data ) ;
315356 }
316- buffer . WriteBytes ( System . Text . Encoding . UTF8 . GetBytes ( fileName ) ) ;
317- buffer . WriteByte ( 0 ) ;
318357
319- var data = new byte [ buffer . length ] ;
320- Buffer . BlockCopy ( buffer . buffer , 0 , data , 0 , buffer . length ) ;
321- directory . SizeOfData = data . Length ;
358+ ImageDebugHeaderEntry pdbChecksumEntry ;
359+ {
360+ var pdbChecksumDirectory = new ImageDebugDirectory ( ) {
361+ MajorVersion = 1 ,
362+ MinorVersion = 0 ,
363+ Type = ImageDebugType . PdbChecksum ,
364+ TimeDateStamp = 0
365+ } ;
322366
323- return new ImageDebugHeader ( new ImageDebugHeaderEntry ( directory , data ) ) ;
324- }
367+ var buffer = new ByteBuffer ( ) ;
368+ // SHA256 - Algorithm name
369+ buffer . WriteBytes ( System . Text . Encoding . UTF8 . GetBytes ( "SHA256" ) ) ;
370+ buffer . WriteByte ( 0 ) ;
325371
326- public void Write ( MethodDebugInformation info )
327- {
328- CheckMethodDebugInformationTable ( ) ;
372+ // Checksum - 32 bytes
373+ buffer . WriteBytes ( pdb_checksum ) ;
329374
330- pdb_metadata . AddMethodDebugInformation ( info ) ;
375+ var data = new byte [ buffer . length ] ;
376+ Buffer . BlockCopy ( buffer . buffer , 0 , data , 0 , buffer . length ) ;
377+ pdbChecksumDirectory . SizeOfData = data . Length ;
378+
379+ pdbChecksumEntry = new ImageDebugHeaderEntry ( pdbChecksumDirectory , data ) ;
380+ }
381+
382+ return new ImageDebugHeader ( new ImageDebugHeaderEntry [ ] { codeViewEntry , pdbChecksumEntry } ) ;
331383 }
332384
333385 void CheckMethodDebugInformationTable ( )
@@ -343,10 +395,8 @@ void CheckMethodDebugInformationTable ()
343395
344396 public void Dispose ( )
345397 {
346- if ( IsEmbedded )
347- return ;
348-
349- WritePdbFile ( ) ;
398+ writer . stream . Dispose ( ) ;
399+ final_stream . Dispose ( ) ;
350400 }
351401
352402 void WritePdbFile ( )
@@ -360,15 +410,18 @@ void WritePdbFile ()
360410 writer . WriteMetadata ( ) ;
361411
362412 writer . Flush ( ) ;
363- writer . stream . Dispose ( ) ;
413+
414+ ComputeChecksumAndPdbId ( ) ;
415+
416+ WritePdbId ( ) ;
364417 }
365418
366419 void WritePdbHeap ( )
367420 {
368421 var pdb_heap = pdb_metadata . pdb_heap ;
369422
370- pdb_heap . WriteBytes ( module . Mvid . ToByteArray ( ) ) ;
371- pdb_heap . WriteUInt32 ( module_metadata . timestamp ) ;
423+ // PDB ID ( GUID + TimeStamp ) are left zeroed out for now. Will be filled at the end with a hash.
424+ pdb_heap . WriteBytes ( 20 ) ;
372425
373426 pdb_heap . WriteUInt32 ( module_metadata . entry_point . ToUInt32 ( ) ) ;
374427
@@ -399,6 +452,32 @@ void WriteTableHeap ()
399452 pdb_metadata . table_heap . ComputeTableInformations ( ) ;
400453 pdb_metadata . table_heap . WriteTableHeap ( ) ;
401454 }
455+
456+ void ComputeChecksumAndPdbId ( )
457+ {
458+ var buffer = new byte [ 8192 ] ;
459+
460+ // Compute the has of the entire file - PDB ID is zeroes still
461+ writer . BaseStream . Seek ( 0 , SeekOrigin . Begin ) ;
462+ var sha256 = SHA256 . Create ( ) ;
463+ using ( var crypto_stream = new CryptoStream ( Stream . Null , sha256 , CryptoStreamMode . Write ) ) {
464+ CryptoService . CopyStreamChunk ( writer . BaseStream , crypto_stream , buffer , ( int ) writer . BaseStream . Length ) ;
465+ }
466+
467+ pdb_checksum = sha256 . Hash ;
468+
469+ var hashBytes = new ByteBuffer ( pdb_checksum ) ;
470+ pdb_id_guid = new Guid ( hashBytes . ReadBytes ( 16 ) ) ;
471+ pdb_id_stamp = hashBytes . ReadUInt32 ( ) ;
472+ }
473+
474+ void WritePdbId ( )
475+ {
476+ // PDB ID is the first 20 bytes of the PdbHeap
477+ writer . MoveToRVA ( TextSegment . PdbHeap ) ;
478+ writer . WriteBytes ( pdb_id_guid . ToByteArray ( ) ) ;
479+ writer . WriteUInt32 ( pdb_id_stamp ) ;
480+ }
402481 }
403482
404483 public sealed class EmbeddedPortablePdbWriterProvider : ISymbolWriterProvider {
@@ -435,9 +514,14 @@ public ISymbolReaderProvider GetReaderProvider ()
435514 return new EmbeddedPortablePdbReaderProvider ( ) ;
436515 }
437516
517+ public void Write ( MethodDebugInformation info )
518+ {
519+ writer . Write ( info ) ;
520+ }
521+
438522 public ImageDebugHeader GetDebugHeader ( )
439523 {
440- writer . Dispose ( ) ;
524+ ImageDebugHeader pdbDebugHeader = writer . GetDebugHeader ( ) ;
441525
442526 var directory = new ImageDebugDirectory {
443527 Type = ImageDebugType . EmbeddedPortablePdb ,
@@ -462,19 +546,22 @@ public ImageDebugHeader GetDebugHeader ()
462546
463547 directory . SizeOfData = ( int ) data . Length ;
464548
465- return new ImageDebugHeader ( new [ ] {
466- writer . GetDebugHeader ( ) . Entries [ 0 ] ,
467- new ImageDebugHeaderEntry ( directory , data . ToArray ( ) )
468- } ) ;
549+ var debugHeaderEntries = new ImageDebugHeaderEntry [ pdbDebugHeader . Entries . Length + 1 ] ;
550+ for ( int i = 0 ; i < pdbDebugHeader . Entries . Length ; i ++ )
551+ debugHeaderEntries [ i ] = pdbDebugHeader . Entries [ i ] ;
552+ debugHeaderEntries [ debugHeaderEntries . Length - 1 ] = new ImageDebugHeaderEntry ( directory , data . ToArray ( ) ) ;
553+
554+ return new ImageDebugHeader ( debugHeaderEntries ) ;
469555 }
470556
471- public void Write ( MethodDebugInformation info )
557+ public void Write ( )
472558 {
473- writer . Write ( info ) ;
559+ writer . Write ( ) ;
474560 }
475561
476562 public void Dispose ( )
477563 {
564+ writer . Dispose ( ) ;
478565 }
479566 }
480567
0 commit comments