obfuz/Plugins/dnlib/DotNet/Writer/DebugDirectory.cs

175 lines
5.0 KiB
C#

// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using dnlib.DotNet.Pdb;
using dnlib.IO;
using dnlib.PE;
namespace dnlib.DotNet.Writer {
/// <summary>
/// Debug directory entry
/// </summary>
public sealed class DebugDirectoryEntry {
/// <summary>
/// Gets the header
/// </summary>
public IMAGE_DEBUG_DIRECTORY DebugDirectory;
/// <summary>
/// Gets the data
/// </summary>
public readonly IChunk Chunk;
/// <summary>
/// Constructor
/// </summary>
/// <param name="chunk">Data</param>
public DebugDirectoryEntry(IChunk chunk) => Chunk = chunk;
}
/// <summary>
/// Debug directory chunk
/// </summary>
public sealed class DebugDirectory : IReuseChunk {
/// <summary>Default debug directory alignment</summary>
public const uint DEFAULT_DEBUGDIRECTORY_ALIGNMENT = 4;
internal const int HEADER_SIZE = 28;
internal int Count => entries.Count;
FileOffset offset;
RVA rva;
uint length;
readonly List<DebugDirectoryEntry> entries;
bool isReadonly;
/// <inheritdoc/>
public FileOffset FileOffset => offset;
/// <inheritdoc/>
public RVA RVA => rva;
/// <summary>
/// Constructor
/// </summary>
public DebugDirectory() => entries = new List<DebugDirectoryEntry>();
/// <summary>
/// Adds data
/// </summary>
/// <param name="data">Data</param>
/// <returns></returns>
public DebugDirectoryEntry Add(byte[] data) => Add(new ByteArrayChunk(data));
/// <summary>
/// Adds data
/// </summary>
/// <param name="chunk">Data</param>
/// <returns></returns>
public DebugDirectoryEntry Add(IChunk chunk) {
if (isReadonly)
throw new InvalidOperationException("Can't add a new DebugDirectory entry when the DebugDirectory is read-only!");
var entry = new DebugDirectoryEntry(chunk);
entries.Add(entry);
return entry;
}
/// <summary>
/// Adds data
/// </summary>
/// <param name="data">Data</param>
/// <param name="type">Debug type</param>
/// <param name="majorVersion">Major version</param>
/// <param name="minorVersion">Minor version</param>
/// <param name="timeDateStamp">Timestamp</param>
/// <returns></returns>
public DebugDirectoryEntry Add(byte[] data, ImageDebugType type, ushort majorVersion, ushort minorVersion, uint timeDateStamp) =>
Add(new ByteArrayChunk(data), type, majorVersion, minorVersion, timeDateStamp);
/// <summary>
/// Adds data
/// </summary>
/// <param name="chunk">Data</param>
/// <param name="type">Debug type</param>
/// <param name="majorVersion">Major version</param>
/// <param name="minorVersion">Minor version</param>
/// <param name="timeDateStamp">Timestamp</param>
/// <returns></returns>
public DebugDirectoryEntry Add(IChunk chunk, ImageDebugType type, ushort majorVersion, ushort minorVersion, uint timeDateStamp) {
var entry = Add(chunk);
entry.DebugDirectory.Type = type;
entry.DebugDirectory.MajorVersion = majorVersion;
entry.DebugDirectory.MinorVersion = minorVersion;
entry.DebugDirectory.TimeDateStamp = timeDateStamp;
return entry;
}
bool IReuseChunk.CanReuse(RVA origRva, uint origSize) {
uint newLength = GetLength(entries, (FileOffset)origRva, origRva);
if (newLength > origSize)
return false;
isReadonly = true;
return true;
}
/// <inheritdoc/>
public void SetOffset(FileOffset offset, RVA rva) {
isReadonly = true;
this.offset = offset;
this.rva = rva;
length = GetLength(entries, offset, rva);
}
static uint GetLength(List<DebugDirectoryEntry> entries, FileOffset offset, RVA rva) {
uint length = HEADER_SIZE * (uint)entries.Count;
foreach (var entry in entries) {
length = Utils.AlignUp(length, DEFAULT_DEBUGDIRECTORY_ALIGNMENT);
entry.Chunk.SetOffset(offset + length, rva + length);
length += entry.Chunk.GetFileLength();
}
return length;
}
/// <inheritdoc/>
public uint GetFileLength() => length;
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();
/// <inheritdoc/>
public uint CalculateAlignment() => 0;
/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
uint offset = 0;
foreach (var entry in entries) {
writer.WriteUInt32(entry.DebugDirectory.Characteristics);
writer.WriteUInt32(entry.DebugDirectory.TimeDateStamp);
writer.WriteUInt16(entry.DebugDirectory.MajorVersion);
writer.WriteUInt16(entry.DebugDirectory.MinorVersion);
writer.WriteUInt32((uint)entry.DebugDirectory.Type);
uint length = entry.Chunk.GetFileLength();
writer.WriteUInt32(length);
writer.WriteUInt32(length == 0 ? 0 : (uint)entry.Chunk.RVA);
writer.WriteUInt32(length == 0 ? 0 : (uint)entry.Chunk.FileOffset);
offset += HEADER_SIZE;
}
foreach (var entry in entries) {
WriteAlign(writer, ref offset);
entry.Chunk.VerifyWriteTo(writer);
offset += entry.Chunk.GetFileLength();
}
}
static void WriteAlign(DataWriter writer, ref uint offs) {
uint align = Utils.AlignUp(offs, DEFAULT_DEBUGDIRECTORY_ALIGNMENT) - offs;
offs += align;
writer.WriteZeroes((int)align);
}
}
}