// 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); } } }