@@ -499,45 +499,78 @@ const Msf = struct {
499
499
500
500
const superblock = try in .readStruct (SuperBlock );
501
501
502
+ // Sanity checks
502
503
if (! mem .eql (u8 , superblock .FileMagic , SuperBlock .file_magic ))
503
504
return error .InvalidDebugInfo ;
504
-
505
+ if (superblock .FreeBlockMapBlock != 1 and superblock .FreeBlockMapBlock != 2 )
506
+ return error .InvalidDebugInfo ;
507
+ if (superblock .NumBlocks * superblock .BlockSize != try file .getEndPos ())
508
+ return error .InvalidDebugInfo ;
505
509
switch (superblock .BlockSize ) {
506
510
// llvm only supports 4096 but we can handle any of these values
507
511
512 , 1024 , 2048 , 4096 = > {},
508
512
else = > return error .InvalidDebugInfo ,
509
513
}
510
514
511
- if (superblock .NumBlocks * superblock .BlockSize != try file .getEndPos ())
512
- return error .InvalidDebugInfo ;
515
+ const dir_block_count = blockCountFromSize (superblock .NumDirectoryBytes , superblock .BlockSize );
516
+ if (dir_block_count > superblock .BlockSize / @sizeOf (u32 ))
517
+ return error .UnhandledBigDirectoryStream ; // cf. BlockMapAddr comment.
513
518
514
- self .directory = try MsfStream .init (
519
+ try file .seekTo (superblock .BlockSize * superblock .BlockMapAddr );
520
+ var dir_blocks = try allocator .alloc (u32 , dir_block_count );
521
+ for (dir_blocks ) | * b | {
522
+ b .* = try in .readIntLittle (u32 );
523
+ }
524
+ self .directory = MsfStream .init (
515
525
superblock .BlockSize ,
516
- blockCountFromSize (superblock .NumDirectoryBytes , superblock .BlockSize ),
517
- superblock .BlockSize * superblock .BlockMapAddr ,
518
526
file ,
519
- allocator ,
527
+ dir_blocks ,
520
528
);
521
529
530
+ const begin = self .directory .pos ;
522
531
const stream_count = try self .directory .stream .readIntLittle (u32 );
523
-
524
532
const stream_sizes = try allocator .alloc (u32 , stream_count );
525
- for (stream_sizes ) | * s | {
533
+ defer allocator .free (stream_sizes );
534
+
535
+ // Microsoft's implementation uses u32(-1) for inexistant streams.
536
+ // These streams are not used, but still participate in the file
537
+ // and must be taken into account when resolving stream indices.
538
+ const Nil = 0xFFFFFFFF ;
539
+ for (stream_sizes ) | * s , i | {
526
540
const size = try self .directory .stream .readIntLittle (u32 );
527
- s .* = blockCountFromSize (size , superblock .BlockSize );
541
+ s .* = if ( size == Nil ) 0 else blockCountFromSize (size , superblock .BlockSize );
528
542
}
529
543
530
544
self .streams = try allocator .alloc (MsfStream , stream_count );
531
545
for (self .streams ) | * stream , i | {
532
- stream .* = try MsfStream .init (
533
- superblock .BlockSize ,
534
- stream_sizes [i ],
535
- // MsfStream.init expects the file to be at the part where it reads [N]u32
536
- try file .getPos (),
537
- file ,
538
- allocator ,
539
- );
546
+ const size = stream_sizes [i ];
547
+ if (size == 0 ) {
548
+ stream .* = MsfStream {
549
+ .blocks = [_ ]u32 {},
550
+ };
551
+ } else {
552
+ var blocks = try allocator .alloc (u32 , size );
553
+ var j : u32 = 0 ;
554
+ while (j < size ) : (j += 1 ) {
555
+ const block_id = try self .directory .stream .readIntLittle (u32 );
556
+ const n = (block_id % superblock .BlockSize );
557
+ // 0 is for SuperBlock, 1 and 2 for FPMs.
558
+ if (block_id == 0 or n == 1 or n == 2 or block_id * superblock .BlockSize > try file .getEndPos ())
559
+ return error .InvalidBlockIndex ;
560
+ blocks [j ] = block_id ;
561
+ }
562
+
563
+ stream .* = MsfStream .init (
564
+ superblock .BlockSize ,
565
+ file ,
566
+ blocks ,
567
+ );
568
+ }
540
569
}
570
+
571
+ const end = self .directory .pos ;
572
+ if (end - begin != superblock .NumDirectoryBytes )
573
+ return error .InvalidStreamDirectory ;
541
574
}
542
575
};
543
576
@@ -574,7 +607,6 @@ const SuperBlock = packed struct {
574
607
NumDirectoryBytes : u32 ,
575
608
576
609
Unknown : u32 ,
577
-
578
610
/// The index of a block within the MSF file. At this block is an array of
579
611
/// ulittle32_t’s listing the blocks that the stream directory resides on.
580
612
/// For large MSF files, the stream directory (which describes the block
@@ -584,45 +616,41 @@ const SuperBlock = packed struct {
584
616
/// and the stream directory itself can be stitched together accordingly.
585
617
/// The number of ulittle32_t’s in this array is given by
586
618
/// ceil(NumDirectoryBytes / BlockSize).
619
+ // Note: microsoft-pdb code actually suggests this is a variable-length
620
+ // array. If the indices of blocks occupied by the Stream Directory didn't
621
+ // fit in one page, there would be other u32 following it.
622
+ // This would mean the Stream Directory is bigger than BlockSize / sizeof(u32)
623
+ // blocks. We're not even close to this with a 1GB pdb file, and LLVM didn't
624
+ // implement it so we're kind of safe making this assumption for now.
587
625
BlockMapAddr : u32 ,
588
626
};
589
627
590
628
const MsfStream = struct {
591
- in_file : File ,
592
- pos : u64 ,
593
- blocks : []u32 ,
594
- block_size : u32 ,
629
+ in_file : File = undefined ,
630
+ pos : u64 = undefined ,
631
+ blocks : []u32 = undefined ,
632
+ block_size : u32 = undefined ,
595
633
596
634
/// Implementation of InStream trait for Pdb.MsfStream
597
- stream : Stream ,
635
+ stream : Stream = undefined ,
598
636
599
637
pub const Error = @typeOf (read ).ReturnType .ErrorSet ;
600
638
pub const Stream = io .InStream (Error );
601
639
602
- fn init (block_size : u32 , block_count : u32 , pos : u64 , file : File , allocator : * mem.Allocator ) ! MsfStream {
603
- var stream = MsfStream {
640
+ fn init (block_size : u32 , file : File , blocks : [] u32 ) MsfStream {
641
+ const stream = MsfStream {
604
642
.in_file = file ,
605
643
.pos = 0 ,
606
- .blocks = try allocator . alloc ( u32 , block_count ) ,
644
+ .blocks = blocks ,
607
645
.block_size = block_size ,
608
646
.stream = Stream { .readFn = readFn },
609
647
};
610
648
611
- var file_stream = file .inStream ();
612
- const in = & file_stream .stream ;
613
- try file .seekTo (pos );
614
-
615
- var i : u32 = 0 ;
616
- while (i < block_count ) : (i += 1 ) {
617
- stream .blocks [i ] = try in .readIntLittle (u32 );
618
- }
619
-
620
649
return stream ;
621
650
}
622
651
623
652
fn readNullTermString (self : * MsfStream , allocator : * mem.Allocator ) ! []u8 {
624
653
var list = ArrayList (u8 ).init (allocator );
625
- defer list .deinit ();
626
654
while (true ) {
627
655
const byte = try self .stream .readByte ();
628
656
if (byte == 0 ) {
@@ -642,11 +670,12 @@ const MsfStream = struct {
642
670
const in = & file_stream .stream ;
643
671
644
672
var size : usize = 0 ;
645
- for (buffer ) | * byte | {
646
- byte .* = try in .readByte ();
647
-
648
- offset += 1 ;
649
- size += 1 ;
673
+ var rem_buffer = buffer ;
674
+ while (size < buffer .len ) {
675
+ const size_to_read = math .min (self .block_size - offset , rem_buffer .len );
676
+ size += try in .read (rem_buffer [0.. size_to_read ]);
677
+ rem_buffer = buffer [size .. ];
678
+ offset += size_to_read ;
650
679
651
680
// If we're at the end of a block, go to the next one.
652
681
if (offset == self .block_size ) {
@@ -657,8 +686,8 @@ const MsfStream = struct {
657
686
}
658
687
}
659
688
660
- self .pos += size ;
661
- return size ;
689
+ self .pos += buffer . len ;
690
+ return buffer . len ;
662
691
}
663
692
664
693
fn seekBy (self : * MsfStream , len : i64 ) ! void {
0 commit comments