Index: include/llvm/ADT/BitVector.h =================================================================== --- include/llvm/ADT/BitVector.h +++ include/llvm/ADT/BitVector.h @@ -466,6 +466,35 @@ std::swap(Capacity, RHS.Capacity); } + // Returns the bitmap as a vector in the little-endian byte order. + std::vector getAsByteVector() { + static_assert(BITWORD_SIZE == 64 || BITWORD_SIZE == 32, + "Unsupported word size"); + + std::vector Vec(Size / 8); + if (BITWORD_SIZE == 64) { + for (unsigned I = 0, E = Size / 64; I != E; ++I) { + Vec[I * 8] = uint8_t(Bits[I] >> 56); + Vec[I * 8 + 1] = uint8_t(Bits[I] >> 48); + Vec[I * 8 + 2] = uint8_t(Bits[I] >> 40); + Vec[I * 8 + 3] = uint8_t(Bits[I] >> 32); + Vec[I * 8 + 4] = uint8_t(Bits[I] >> 24); + Vec[I * 8 + 5] = uint8_t(Bits[I] >> 16); + Vec[I * 8 + 6] = uint8_t(Bits[I] >> 8); + Vec[I * 8 + 7] = uint8_t(Bits[I]); + } + } else { + for (unsigned I = 0, E = Size / 64; I != E; ++I) { + Vec[I * 4] = uint8_t(Bits[I] >> 24); + Vec[I * 4 + 1] = uint8_t(Bits[I] >> 16); + Vec[I * 4 + 2] = uint8_t(Bits[I] >> 8); + Vec[I * 4 + 3] = uint8_t(Bits[I]); + } + return Vec; + } + return Vec; + } + //===--------------------------------------------------------------------===// // Portable bit mask operations. //===--------------------------------------------------------------------===// Index: lib/DebugInfo/MSF/MSFBuilder.cpp =================================================================== --- lib/DebugInfo/MSF/MSFBuilder.cpp +++ lib/DebugInfo/MSF/MSFBuilder.cpp @@ -253,6 +253,22 @@ // grow. SB->NumBlocks = FreeBlocks.size(); + // Allocate discontiguous free page map blocks. If one page is too + // small to store the entire FPM, two or more pages are allocated + // for the FPM. + uint32_t NumFPMs = alignTo(FreeBlocks.size(), BlockSize) / BlockSize; + for (uint32_t I = 0; I != NumFPMs; ++I) { + FreeBlocks[I * BlockSize + kFreePageMap0Block] = false; + FreeBlocks[I * BlockSize + kFreePageMap1Block] = false; + } + + // Copy free page map. Note that on-disk FPM is slightly different from + // in-memory FPM since the pages for stream 0 are marked unused on-disk. + // We need to do that for compatibility. + L.FreePageMap = FreeBlocks; + for (uint32_t BlockNumber : StreamData[0].second) + L.FreePageMap[BlockNumber] = false; + ulittle32_t *DirBlocks = Allocator.Allocate(NumDirectoryBlocks); std::uninitialized_copy_n(DirectoryBlocks.begin(), NumDirectoryBlocks, DirBlocks); Index: lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp +++ lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp @@ -113,6 +113,14 @@ if (auto EC = Writer.writeObject(*Layout.SB)) return EC; + + // Write free page map. + auto FpmStream = WritableMappedBlockStream::createFpmStream(Layout); + StreamWriter FpmWriter(FpmStream); + if (auto EC = FpmWriter.writeBytes(Layout.FreePageMap.getAsByteVector())) + return EC; + + // Write block map. uint32_t BlockMapOffset = msf::blockToOffset(Layout.SB->BlockMapAddr, Layout.SB->BlockSize); Writer.setOffset(BlockMapOffset); @@ -145,4 +153,4 @@ } return Buffer.commit(); -} \ No newline at end of file +} Index: test/DebugInfo/PDB/pdbdump-fpm.test =================================================================== --- /dev/null +++ test/DebugInfo/PDB/pdbdump-fpm.test @@ -0,0 +1,21 @@ +# RUN: llvm-pdbdump yaml2pdb -pdb=%t.pdb %s +# RUN: llvm-pdbdump raw -page-stats %t.pdb | FileCheck %s + +# CHECK: Multiply Used Pages: [] +# CHECK: Use After Free Pages: [] + +MSF: + SuperBlock: + BlockSize: 1024 + FreeBlockMap: 2 + NumBlocks: 2000 + NumDirectoryBytes: 136 + Unknown1: 0 + BlockMapAddr: 24 + NumDirectoryBlocks: 1 + DirectoryBlocks: [ 23 ] + NumStreams: 1 + FileSize: 2048000 +StreamSizes: [ 40 ] +StreamMap: + - Stream: [ 8 ] Index: test/DebugInfo/PDB/pdbdump-readwrite.test =================================================================== --- test/DebugInfo/PDB/pdbdump-readwrite.test +++ test/DebugInfo/PDB/pdbdump-readwrite.test @@ -3,36 +3,51 @@ RUN: %p/Inputs/empty.pdb > %t.1 RUN: llvm-pdbdump yaml2pdb -pdb=%t.2 %t.1 -RUN: llvm-pdbdump raw -headers %p/Inputs/empty.pdb | FileCheck %s -RUN: llvm-pdbdump raw -headers %t.2 | FileCheck %s +RUN: llvm-pdbdump raw -headers %p/Inputs/empty.pdb | FileCheck -check-prefix=HDR %s +RUN: llvm-pdbdump raw -headers %t.2 | FileCheck -check-prefix=HDR %s -CHECK: FileHeaders { -CHECK-NEXT: BlockSize: 4096 -CHECK-NEXT: FreeBlockMap: 2 -CHECK-NEXT: NumBlocks: 25 -CHECK-NEXT: NumDirectoryBytes: 136 -CHECK-NEXT: Unknown1: 0 -CHECK-NEXT: BlockMapAddr: 24 -CHECK-NEXT: NumDirectoryBlocks: 1 -CHECK-NEXT: DirectoryBlocks: [23] -CHECK-NEXT: NumStreams: 17 -CHECK-NEXT: } -CHECK-NEXT: PDB Stream { -CHECK-NEXT: Version: 20000404 -CHECK-NEXT: Signature: 0x54E507E2 -CHECK-NEXT: Age: 1 -CHECK-NEXT: Guid: {0B355641-86A0-A249-896F-9988FAE52FF0} -CHECK-NEXT: } -CHECK-NEXT: DBI Stream { -CHECK-NEXT: Dbi Version: 19990903 -CHECK-NEXT: Age: 1 -CHECK-NEXT: Incremental Linking: Yes -CHECK-NEXT: Has CTypes: No -CHECK-NEXT: Is Stripped: No -CHECK-NEXT: Machine Type: x86 -CHECK-NEXT: Symbol Record Stream Index: -CHECK-NEXT: Public Symbol Stream Index: -CHECK-NEXT: Global Symbol Stream Index: -CHECK-NEXT: Toolchain Version: 12.0 -CHECK-NEXT: mspdb120.dll version: 12.0.31101 -CHECK-NEXT: } +HDR: FileHeaders { +HDR-NEXT: BlockSize: 4096 +HDR-NEXT: FreeBlockMap: 2 +HDR-NEXT: NumBlocks: 25 +HDR-NEXT: NumDirectoryBytes: 136 +HDR-NEXT: Unknown1: 0 +HDR-NEXT: BlockMapAddr: 24 +HDR-NEXT: NumDirectoryBlocks: 1 +HDR-NEXT: DirectoryBlocks: [23] +HDR-NEXT: NumStreams: 17 +HDR-NEXT: } +HDR-NEXT: PDB Stream { +HDR-NEXT: Version: 20000404 +HDR-NEXT: Signature: 0x54E507E2 +HDR-NEXT: Age: 1 +HDR-NEXT: Guid: {0B355641-86A0-A249-896F-9988FAE52FF0} +HDR-NEXT: } +HDR-NEXT: DBI Stream { +HDR-NEXT: Dbi Version: 19990903 +HDR-NEXT: Age: 1 +HDR-NEXT: Incremental Linking: Yes +HDR-NEXT: Has CTypes: No +HDR-NEXT: Is Stripped: No +HDR-NEXT: Machine Type: x86 +HDR-NEXT: Symbol Record Stream Index: +HDR-NEXT: Public Symbol Stream Index: +HDR-NEXT: Global Symbol Stream Index: +HDR-NEXT: Toolchain Version: 12.0 +HDR-NEXT: mspdb120.dll version: 12.0.31101 +HDR-NEXT: } + +RUN: llvm-pdbdump raw -page-stats %p/Inputs/empty.pdb | FileCheck -check-prefix=FPM1 %s +RUN: llvm-pdbdump raw -page-stats %t.2 | FileCheck -check-prefix=FPM2 %s + +FPM1: Msf Free Pages: [3, 4, 5, 8, 9] +FPM1-NEXT: Orphaned Pages: [] +FPM1-NEXT: Multiply Used Pages: [] +FPM1-NEXT: Use After Free Pages: [] + +;; We are not able to correctly restore stream 0. +;; That's why we get a different result. +FPM2: Msf Free Pages: [3, 4, 5, 9] +FPM2-NEXT: Orphaned Pages: [8] +FPM2-NEXT: Multiply Used Pages: [] +FPM2-NEXT: Use After Free Pages: []