// 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 {
///
/// Debug directory entry
///
public sealed class DebugDirectoryEntry {
///
/// Gets the header
///
public IMAGE_DEBUG_DIRECTORY DebugDirectory;
///
/// Gets the data
///
public readonly IChunk Chunk;
///
/// Constructor
///
/// Data
public DebugDirectoryEntry(IChunk chunk) => Chunk = chunk;
}
///
/// Debug directory chunk
///
public sealed class DebugDirectory : IReuseChunk {
/// Default debug directory alignment
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 entries;
bool isReadonly;
///
public FileOffset FileOffset => offset;
///
public RVA RVA => rva;
///
/// Constructor
///
public DebugDirectory() => entries = new List();
///
/// Adds data
///
/// Data
///
public DebugDirectoryEntry Add(byte[] data) => Add(new ByteArrayChunk(data));
///
/// Adds data
///
/// Data
///
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;
}
///
/// Adds data
///
/// Data
/// Debug type
/// Major version
/// Minor version
/// Timestamp
///
public DebugDirectoryEntry Add(byte[] data, ImageDebugType type, ushort majorVersion, ushort minorVersion, uint timeDateStamp) =>
Add(new ByteArrayChunk(data), type, majorVersion, minorVersion, timeDateStamp);
///
/// Adds data
///
/// Data
/// Debug type
/// Major version
/// Minor version
/// Timestamp
///
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;
}
///
public void SetOffset(FileOffset offset, RVA rva) {
isReadonly = true;
this.offset = offset;
this.rva = rva;
length = GetLength(entries, offset, rva);
}
static uint GetLength(List 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;
}
///
public uint GetFileLength() => length;
///
public uint GetVirtualSize() => GetFileLength();
///
public uint CalculateAlignment() => 0;
///
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);
}
}
}