// dnlib: See LICENSE.txt for more info using System.Collections.Generic; using System.Text; using dnlib.IO; using dnlib.PE; using dnlib.DotNet.MD; namespace dnlib.DotNet.Writer { /// /// options /// public sealed class MetadataHeaderOptions { /// /// Default version string /// public const string DEFAULT_VERSION_STRING = MDHeaderRuntimeVersion.MS_CLR_20; /// /// Default header signature /// public const uint DEFAULT_SIGNATURE = 0x424A5342; /// /// MD header signature. Default value is /// public uint? Signature; /// /// Major version. Default is 1. MS' CLR supports v0.x (x >= 19) and v1.1, nothing else. /// public ushort? MajorVersion; /// /// Minor version. Default is 1. /// public ushort? MinorVersion; /// /// Reserved and should be 0. /// public uint? Reserved1; /// /// Version string. Default is . It's stored as a /// zero-terminated UTF-8 string. Length should be <= 255 bytes. /// public string VersionString; /// /// Storage flags should be 0 /// public StorageFlags? StorageFlags; /// /// Reserved and should be 0 /// public byte? Reserved2; /// /// Creates portable PDB v1.0 options /// /// public static MetadataHeaderOptions CreatePortablePdbV1_0() => new MetadataHeaderOptions() { Signature = DEFAULT_SIGNATURE, MajorVersion = 1, MinorVersion = 1, Reserved1 = 0, VersionString = MDHeaderRuntimeVersion.PORTABLE_PDB_V1_0, StorageFlags = 0, Reserved2 = 0, }; } /// /// Meta data header. IMAGE_COR20_HEADER.Metadata points to this header. /// public sealed class MetadataHeader : IChunk { IList heaps; readonly MetadataHeaderOptions options; uint length; FileOffset offset; RVA rva; /// public FileOffset FileOffset => offset; /// public RVA RVA => rva; /// /// Gets/sets the heaps /// public IList Heaps { get => heaps; set => heaps = value; } /// /// Default constructor /// public MetadataHeader() : this(null) { } /// /// Constructor /// /// Options public MetadataHeader(MetadataHeaderOptions options) => this.options = options ?? new MetadataHeaderOptions(); /// public void SetOffset(FileOffset offset, RVA rva) { this.offset = offset; this.rva = rva; length = 16; length += (uint)GetVersionString().Length; length = Utils.AlignUp(length, 4); length += 4; var heaps = this.heaps; int count = heaps.Count; for (int i = 0; i < count; i++) { var heap = heaps[i]; length += 8; length += (uint)GetAsciizName(heap.Name).Length; length = Utils.AlignUp(length, 4); } } /// public uint GetFileLength() => length; /// public uint GetVirtualSize() => GetFileLength(); /// public uint CalculateAlignment() => 0; /// public void WriteTo(DataWriter writer) { writer.WriteUInt32(options.Signature ?? MetadataHeaderOptions.DEFAULT_SIGNATURE); writer.WriteUInt16(options.MajorVersion ?? 1); writer.WriteUInt16(options.MinorVersion ?? 1); writer.WriteUInt32(options.Reserved1 ?? 0); var s = GetVersionString(); writer.WriteInt32(Utils.AlignUp(s.Length, 4)); writer.WriteBytes(s); writer.WriteZeroes(Utils.AlignUp(s.Length, 4) - s.Length); writer.WriteByte((byte)(options.StorageFlags ?? 0)); writer.WriteByte(options.Reserved2 ?? 0); var heaps = this.heaps; writer.WriteUInt16((ushort)heaps.Count); int count = heaps.Count; for (int i = 0; i < count; i++) { var heap = heaps[i]; writer.WriteUInt32((uint)(heap.FileOffset - offset)); writer.WriteUInt32(heap.GetFileLength()); writer.WriteBytes(s = GetAsciizName(heap.Name)); if (s.Length > 32) throw new ModuleWriterException($"Heap name '{heap.Name}' is > 32 bytes"); writer.WriteZeroes(Utils.AlignUp(s.Length, 4) - s.Length); } } byte[] GetVersionString() => Encoding.UTF8.GetBytes((options.VersionString ?? MetadataHeaderOptions.DEFAULT_VERSION_STRING) + "\0"); byte[] GetAsciizName(string s) => Encoding.ASCII.GetBytes(s + "\0"); } }