12
12
using System . Collections . Generic ;
13
13
using System . IO ;
14
14
using System . IO . Compression ;
15
-
15
+ using System . Security . Cryptography ;
16
16
using Mono . Cecil . Metadata ;
17
17
using Mono . Cecil . PE ;
18
18
@@ -39,7 +39,7 @@ public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStre
39
39
40
40
ISymbolReader GetSymbolReader ( ModuleDefinition module , Disposable < Stream > symbolStream , string fileName )
41
41
{
42
- return new PortablePdbReader ( ImageReader . ReadPortablePdb ( symbolStream , fileName ) , module ) ;
42
+ return new PortablePdbReader ( ImageReader . ReadPortablePdb ( symbolStream , fileName , out _ ) , module ) ;
43
43
}
44
44
}
45
45
@@ -234,24 +234,27 @@ public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName)
234
234
Mixin . CheckModule ( module ) ;
235
235
Mixin . CheckFileName ( fileName ) ;
236
236
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 ) ) ;
239
239
}
240
240
241
241
public ISymbolWriter GetSymbolWriter ( ModuleDefinition module , Stream symbolStream )
242
242
{
243
243
Mixin . CheckModule ( module ) ;
244
244
Mixin . CheckStream ( symbolStream ) ;
245
245
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 ) ) ;
247
250
}
248
251
249
- ISymbolWriter GetSymbolWriter ( ModuleDefinition module , Disposable < Stream > stream )
252
+ ISymbolWriter GetSymbolWriter ( ModuleDefinition module , Disposable < Stream > stream , Disposable < Stream > final_stream )
250
253
{
251
254
var metadata = new MetadataBuilder ( module , this ) ;
252
255
var writer = ImageWriter . CreateDebugWriter ( module , metadata , stream ) ;
253
256
254
- return new PortablePdbWriter ( metadata , module , writer ) ;
257
+ return new PortablePdbWriter ( metadata , module , writer , final_stream ) ;
255
258
}
256
259
}
257
260
@@ -260,9 +263,14 @@ public sealed class PortablePdbWriter : ISymbolWriter {
260
263
readonly MetadataBuilder pdb_metadata ;
261
264
readonly ModuleDefinition module ;
262
265
readonly ImageWriter writer ;
266
+ readonly Disposable < Stream > final_stream ;
263
267
264
268
MetadataBuilder module_metadata ;
265
269
270
+ internal byte [ ] pdb_checksum ;
271
+ internal Guid pdb_id_guid ;
272
+ internal uint pdb_id_stamp ;
273
+
266
274
bool IsEmbedded { get { return writer == null ; } }
267
275
268
276
internal PortablePdbWriter ( MetadataBuilder pdb_metadata , ModuleDefinition module )
@@ -278,56 +286,100 @@ internal PortablePdbWriter (MetadataBuilder pdb_metadata, ModuleDefinition modul
278
286
pdb_metadata . AddCustomDebugInformations ( module ) ;
279
287
}
280
288
281
- internal PortablePdbWriter ( MetadataBuilder pdb_metadata , ModuleDefinition module , ImageWriter writer )
289
+ internal PortablePdbWriter ( MetadataBuilder pdb_metadata , ModuleDefinition module , ImageWriter writer , Disposable < Stream > final_stream )
282
290
: this ( pdb_metadata , module )
283
291
{
284
292
this . writer = writer ;
293
+ this . final_stream = final_stream ;
285
294
}
286
295
287
296
public ISymbolReaderProvider GetReaderProvider ( )
288
297
{
289
298
return new PortablePdbReaderProvider ( ) ;
290
299
}
291
300
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
+
292
322
public ImageDebugHeader GetDebugHeader ( )
293
323
{
294
324
if ( IsEmbedded )
295
325
return new ImageDebugHeader ( ) ;
296
326
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 ) ;
315
356
}
316
- buffer . WriteBytes ( System . Text . Encoding . UTF8 . GetBytes ( fileName ) ) ;
317
- buffer . WriteByte ( 0 ) ;
318
357
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
+ } ;
322
366
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 ) ;
325
371
326
- public void Write ( MethodDebugInformation info )
327
- {
328
- CheckMethodDebugInformationTable ( ) ;
372
+ // Checksum - 32 bytes
373
+ buffer . WriteBytes ( pdb_checksum ) ;
329
374
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 } ) ;
331
383
}
332
384
333
385
void CheckMethodDebugInformationTable ( )
@@ -343,10 +395,8 @@ void CheckMethodDebugInformationTable ()
343
395
344
396
public void Dispose ( )
345
397
{
346
- if ( IsEmbedded )
347
- return ;
348
-
349
- WritePdbFile ( ) ;
398
+ writer . stream . Dispose ( ) ;
399
+ final_stream . Dispose ( ) ;
350
400
}
351
401
352
402
void WritePdbFile ( )
@@ -360,15 +410,18 @@ void WritePdbFile ()
360
410
writer . WriteMetadata ( ) ;
361
411
362
412
writer . Flush ( ) ;
363
- writer . stream . Dispose ( ) ;
413
+
414
+ ComputeChecksumAndPdbId ( ) ;
415
+
416
+ WritePdbId ( ) ;
364
417
}
365
418
366
419
void WritePdbHeap ( )
367
420
{
368
421
var pdb_heap = pdb_metadata . pdb_heap ;
369
422
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 ) ;
372
425
373
426
pdb_heap . WriteUInt32 ( module_metadata . entry_point . ToUInt32 ( ) ) ;
374
427
@@ -399,6 +452,32 @@ void WriteTableHeap ()
399
452
pdb_metadata . table_heap . ComputeTableInformations ( ) ;
400
453
pdb_metadata . table_heap . WriteTableHeap ( ) ;
401
454
}
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
+ }
402
481
}
403
482
404
483
public sealed class EmbeddedPortablePdbWriterProvider : ISymbolWriterProvider {
@@ -435,9 +514,14 @@ public ISymbolReaderProvider GetReaderProvider ()
435
514
return new EmbeddedPortablePdbReaderProvider ( ) ;
436
515
}
437
516
517
+ public void Write ( MethodDebugInformation info )
518
+ {
519
+ writer . Write ( info ) ;
520
+ }
521
+
438
522
public ImageDebugHeader GetDebugHeader ( )
439
523
{
440
- writer . Dispose ( ) ;
524
+ ImageDebugHeader pdbDebugHeader = writer . GetDebugHeader ( ) ;
441
525
442
526
var directory = new ImageDebugDirectory {
443
527
Type = ImageDebugType . EmbeddedPortablePdb ,
@@ -462,19 +546,22 @@ public ImageDebugHeader GetDebugHeader ()
462
546
463
547
directory . SizeOfData = ( int ) data . Length ;
464
548
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 ) ;
469
555
}
470
556
471
- public void Write ( MethodDebugInformation info )
557
+ public void Write ( )
472
558
{
473
- writer . Write ( info ) ;
559
+ writer . Write ( ) ;
474
560
}
475
561
476
562
public void Dispose ( )
477
563
{
564
+ writer . Dispose ( ) ;
478
565
}
479
566
}
480
567
0 commit comments