-
Notifications
You must be signed in to change notification settings - Fork 130
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #324 from Washi1337/feature/msf-files
Basic read/write support for MSF 7.0 files
- Loading branch information
Showing
20 changed files
with
1,337 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
src/AsmResolver.Symbols.WindowsPdb/AsmResolver.Symbols.WindowsPdb.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<Title>AsmResolver</Title> | ||
<Description>Windows PDB models for the AsmResolver executable file inspection toolsuite.</Description> | ||
<PackageTags>windows pdb symbols</PackageTags> | ||
<Nullable>enable</Nullable> | ||
<TargetFrameworks>net6.0;netcoreapp3.1;netstandard2.0</TargetFrameworks> | ||
<IsTrimmable>true</IsTrimmable> | ||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> | ||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
<DocumentationFile>bin\Debug\netstandard2.0\AsmResolver.Symbols.WindowsPdb.xml</DocumentationFile> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> | ||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
<DocumentationFile>bin\Release\netstandard2.0\AsmResolver.xml</DocumentationFile> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\AsmResolver\AsmResolver.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
38 changes: 38 additions & 0 deletions
38
src/AsmResolver.Symbols.WindowsPdb/Msf/Builder/FreeBlockMap.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using System.Collections; | ||
using AsmResolver.IO; | ||
|
||
namespace AsmResolver.Symbols.WindowsPdb.Msf.Builder; | ||
|
||
/// <summary> | ||
/// Represents a block within a MSF file that contains information on which blocks in the MSF file are free to use. | ||
/// </summary> | ||
public class FreeBlockMap : SegmentBase | ||
{ | ||
/// <summary> | ||
/// Creates a new empty free block map. | ||
/// </summary> | ||
/// <param name="blockSize">The size of a single block in the MSF file.</param> | ||
public FreeBlockMap(uint blockSize) | ||
{ | ||
BitField = new BitArray((int) blockSize * 8, true); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the bit field indicating which blocks in the MSF file are free to use. | ||
/// </summary> | ||
public BitArray BitField | ||
{ | ||
get; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override uint GetPhysicalSize() => (uint) (BitField.Count / 8); | ||
|
||
/// <inheritdoc /> | ||
public override void Write(IBinaryStreamWriter writer) | ||
{ | ||
byte[] data = new byte[BitField.Count / 8]; | ||
BitField.CopyTo(data, 0); | ||
writer.WriteBytes(data); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/AsmResolver.Symbols.WindowsPdb/Msf/Builder/IMsfFileBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
namespace AsmResolver.Symbols.WindowsPdb.Msf.Builder; | ||
|
||
/// <summary> | ||
/// Provides members for constructing new MSF files. | ||
/// </summary> | ||
public interface IMsfFileBuilder | ||
{ | ||
/// <summary> | ||
/// Reconstructs a new writable MSF file buffer from an instance of <see cref="MsfFile"/>. | ||
/// </summary> | ||
/// <param name="file">The file to reconstruct.</param> | ||
/// <returns>The reconstructed buffer.</returns> | ||
MsfFileBuffer CreateFile(MsfFile file); | ||
} |
205 changes: 205 additions & 0 deletions
205
src/AsmResolver.Symbols.WindowsPdb/Msf/Builder/MsfFileBuffer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using AsmResolver.IO; | ||
|
||
namespace AsmResolver.Symbols.WindowsPdb.Msf.Builder; | ||
|
||
/// <summary> | ||
/// Represents a mutable buffer for building up a new MSF file. | ||
/// </summary> | ||
public class MsfFileBuffer : SegmentBase | ||
{ | ||
private readonly Dictionary<MsfStream, int[]> _blockIndices = new(); | ||
private readonly List<FreeBlockMap> _freeBlockMaps = new(2); | ||
private readonly List<ISegment?> _blocks; | ||
|
||
/// <summary> | ||
/// Creates a new empty MSF file buffer. | ||
/// </summary> | ||
/// <param name="blockSize">The block size to use.</param> | ||
public MsfFileBuffer(uint blockSize) | ||
{ | ||
SuperBlock = new MsfSuperBlock | ||
{ | ||
Signature = MsfSuperBlock.BigMsfSignature, | ||
BlockSize = blockSize, | ||
FreeBlockMapIndex = 1, | ||
BlockCount = 3, | ||
}; | ||
|
||
_blocks = new List<ISegment?>((int) blockSize); | ||
|
||
InsertBlock(0, SuperBlock); | ||
var fpm = GetOrCreateFreeBlockMap(1, out _); | ||
InsertBlock(2, null); | ||
|
||
fpm.BitField[0] = false; | ||
fpm.BitField[1] = false; | ||
fpm.BitField[2] = false; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the super block of the MSF file that is being constructed. | ||
/// </summary> | ||
public MsfSuperBlock SuperBlock | ||
{ | ||
get; | ||
} | ||
|
||
/// <summary> | ||
/// Determines whether a block in the MSF file buffer is available or not. | ||
/// </summary> | ||
/// <param name="blockIndex">The index of the block.</param> | ||
/// <returns><c>true</c> if the block is available, <c>false</c> otherwise.</returns> | ||
public bool BlockIsAvailable(int blockIndex) | ||
{ | ||
var freeBlockMap = GetOrCreateFreeBlockMap(blockIndex, out int offset); | ||
if (offset < 3 && (blockIndex == 0 || offset > 0)) | ||
return false; | ||
return freeBlockMap.BitField[offset]; | ||
} | ||
|
||
/// <summary> | ||
/// Inserts a block of the provided MSF stream into the buffer. | ||
/// </summary> | ||
/// <param name="blockIndex">The MSF file index to insert the block into.</param> | ||
/// <param name="stream">The stream to pull a chunk from.</param> | ||
/// <param name="chunkIndex">The index of the chunk to store at the provided block index.</param> | ||
/// <exception cref="ArgumentException"> | ||
/// Occurs when the index provided by <paramref name="blockIndex"/> is already in use. | ||
/// </exception> | ||
public void InsertBlock(int blockIndex, MsfStream stream, int chunkIndex) | ||
{ | ||
var fpm = GetOrCreateFreeBlockMap(blockIndex, out int offset); | ||
if (!fpm.BitField[offset]) | ||
throw new ArgumentException($"Block {blockIndex} is already in use."); | ||
|
||
uint blockSize = SuperBlock.BlockSize; | ||
var segment = new DataSourceSegment( | ||
stream.Contents, | ||
stream.Contents.BaseAddress + (ulong) (chunkIndex * blockSize), | ||
(uint) (chunkIndex * blockSize), | ||
(uint) Math.Min(stream.Contents.Length - (ulong) (chunkIndex * blockSize), blockSize)); | ||
|
||
InsertBlock(blockIndex, segment); | ||
|
||
int[] indices = GetMutableBlockIndicesForStream(stream); | ||
indices[chunkIndex] = blockIndex; | ||
|
||
fpm.BitField[offset] = false; | ||
} | ||
|
||
private void InsertBlock(int blockIndex, ISegment? segment) | ||
{ | ||
// Ensure enough blocks are present in the backing-buffer. | ||
while (_blocks.Count <= blockIndex) | ||
_blocks.Add(null); | ||
|
||
// Insert block and update super block. | ||
_blocks[blockIndex] = segment; | ||
SuperBlock.BlockCount = (uint) _blocks.Count; | ||
} | ||
|
||
private FreeBlockMap GetOrCreateFreeBlockMap(int blockIndex, out int offset) | ||
{ | ||
int index = Math.DivRem(blockIndex, (int) SuperBlock.BlockSize, out offset); | ||
while (_freeBlockMaps.Count <= index) | ||
{ | ||
var freeBlockMap = new FreeBlockMap(SuperBlock.BlockSize); | ||
_freeBlockMaps.Add(freeBlockMap); | ||
InsertBlock(index + (int) SuperBlock.FreeBlockMapIndex, freeBlockMap); | ||
} | ||
|
||
return _freeBlockMaps[index]; | ||
} | ||
|
||
private int[] GetMutableBlockIndicesForStream(MsfStream stream) | ||
{ | ||
if (!_blockIndices.TryGetValue(stream, out int[]? indices)) | ||
{ | ||
indices = new int[stream.GetRequiredBlockCount(SuperBlock.BlockSize)]; | ||
_blockIndices.Add(stream, indices); | ||
} | ||
|
||
return indices; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the allocated indices for the provided MSF stream. | ||
/// </summary> | ||
/// <param name="stream">The stream.</param> | ||
/// <returns>The block indices.</returns> | ||
public int[] GetBlockIndicesForStream(MsfStream stream) => (int[]) GetMutableBlockIndicesForStream(stream).Clone(); | ||
|
||
/// <summary> | ||
/// Constructs a new MSF stream containing the stream directory. | ||
/// </summary> | ||
/// <param name="streams">The files that the directory should list.</param> | ||
/// <returns>The constructed stream.</returns> | ||
/// <remarks> | ||
/// This method does <b>not</b> add the stream to the buffer, nor does it update the super block. | ||
/// </remarks> | ||
public MsfStream CreateStreamDirectory(IList<MsfStream> streams) | ||
{ | ||
using var contents = new MemoryStream(); | ||
var writer = new BinaryStreamWriter(contents); | ||
|
||
// Stream count. | ||
writer.WriteInt32(streams.Count); | ||
|
||
// Stream sizes. | ||
for (int i = 0; i < streams.Count; i++) | ||
writer.WriteUInt32((uint) streams[i].Contents.Length); | ||
|
||
// Stream indices. | ||
for (int i = 0; i < streams.Count; i++) | ||
{ | ||
int[] indices = GetMutableBlockIndicesForStream(streams[i]); | ||
foreach (int index in indices) | ||
writer.WriteInt32(index); | ||
} | ||
|
||
return new MsfStream(contents.ToArray()); | ||
} | ||
|
||
/// <summary> | ||
/// Creates a new MSF stream containing the block indices of the stream directory. | ||
/// </summary> | ||
/// <param name="streamDirectory">The stream directory to store the indices for.</param> | ||
/// <returns>The constructed stream.</returns> | ||
/// <remarks> | ||
/// This method does <b>not</b> add the stream to the buffer, nor does it update the super block. | ||
/// </remarks> | ||
public MsfStream CreateStreamDirectoryMap(MsfStream streamDirectory) | ||
{ | ||
using var contents = new MemoryStream(); | ||
var writer = new BinaryStreamWriter(contents); | ||
|
||
int[] indices = GetMutableBlockIndicesForStream(streamDirectory); | ||
foreach (int index in indices) | ||
writer.WriteInt32(index); | ||
|
||
return new MsfStream(contents.ToArray()); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override uint GetPhysicalSize() => SuperBlock.BlockCount * SuperBlock.BlockSize; | ||
|
||
/// <inheritdoc /> | ||
public override void Write(IBinaryStreamWriter writer) | ||
{ | ||
foreach (var block in _blocks) | ||
{ | ||
if (block is null) | ||
{ | ||
writer.WriteZeroes((int) SuperBlock.BlockSize); | ||
} | ||
else | ||
{ | ||
block.Write(writer); | ||
writer.Align(SuperBlock.BlockSize); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.