public void WriteUTF8() { var writer = new BlobBuilder(4); writer.WriteUTF8("a"); writer.WriteUTF8(""); writer.WriteUTF8("bc"); writer.WriteUTF8("d"); writer.WriteUTF8(""); writer.WriteUTF8(Encoding.UTF8.GetString(new byte[] { 0x00, 0xC2, 0x80, 0xE1, 0x88, 0xB4 })); writer.WriteUTF8("\0\ud800"); // hi surrogate writer.WriteUTF8("\0\udc00"); // lo surrogate writer.WriteUTF8("\0\ud800\udc00"); // pair writer.WriteUTF8("\0\udc00\ud800"); // lo + hi AssertEx.Equal(new byte[] { (byte)'a', (byte)'b', (byte)'c', (byte)'d', 0x00, 0xC2, 0x80, 0xE1, 0x88, 0xB4, 0x00, 0xED, 0xA0, 0x80, 0x00, 0xED, 0xB0, 0x80, 0x00, 0xF0, 0x90, 0x80, 0x80, 0x00, 0xED, 0xB0, 0x80, 0xED, 0xA0, 0x80 }, writer.ToArray()); }
public unsafe void Write_Errors() { var builder = new BlobBuilder(16); Assert.Throws <ArgumentNullException>(() => builder.WriteUTF16((char[])null)); Assert.Throws <ArgumentNullException>(() => builder.WriteUTF16((string)null)); Assert.Throws <ArgumentNullException>(() => builder.WriteUTF8(null, allowUnpairedSurrogates: true)); Assert.Throws <ArgumentNullException>(() => builder.WriteUTF8(null, allowUnpairedSurrogates: true)); Assert.Throws <ArgumentNullException>(() => builder.TryWriteBytes((Stream)null, 0)); Assert.Throws <ArgumentNullException>(() => builder.WriteBytes(null)); Assert.Throws <ArgumentNullException>(() => builder.WriteBytes(null, 0, 0)); Assert.Throws <ArgumentNullException>(() => builder.WriteBytes((byte *)null, 0)); Assert.Throws <ArgumentNullException>(() => builder.WriteBytes(default(ImmutableArray <byte>))); Assert.Throws <ArgumentNullException>(() => builder.WriteBytes(default(ImmutableArray <byte>), 0, 0)); var bw = default(BlobWriter); Assert.Throws <ArgumentNullException>(() => builder.WriteContentTo(ref bw)); Assert.Throws <ArgumentNullException>(() => builder.WriteContentTo((Stream)null)); Assert.Throws <ArgumentNullException>(() => builder.WriteContentTo((BlobBuilder)null)); Assert.Throws <ArgumentOutOfRangeException>(() => builder.TryWriteBytes(new MemoryStream(), -1)); Assert.Throws <ArgumentOutOfRangeException>(() => builder.WriteBytes(0, -1)); Assert.Throws <ArgumentOutOfRangeException>(() => builder.WriteBytes(new byte[] { }, 1, 0)); Assert.Throws <ArgumentOutOfRangeException>(() => builder.WriteBytes(new byte[] { }, 0, 1)); Assert.Throws <ArgumentOutOfRangeException>(() => builder.WriteBytes(new byte[] { }, 0, -1)); Assert.Throws <ArgumentOutOfRangeException>(() => builder.WriteBytes(ImmutableArray <byte> .Empty, 1, 0)); Assert.Throws <ArgumentOutOfRangeException>(() => builder.WriteBytes(ImmutableArray <byte> .Empty, 0, 1)); Assert.Throws <ArgumentOutOfRangeException>(() => builder.WriteBytes(ImmutableArray <byte> .Empty, 1, -1)); }
public void WriteUTF8_ReplaceUnpairedSurrogates() { var writer = new BlobBuilder(4); writer.WriteUTF8("a", allowUnpairedSurrogates: false); writer.WriteUTF8("", allowUnpairedSurrogates: false); writer.WriteUTF8("bc", allowUnpairedSurrogates: false); writer.WriteUTF8("d", allowUnpairedSurrogates: false); writer.WriteUTF8("", allowUnpairedSurrogates: false); writer.WriteUTF8(Encoding.UTF8.GetString(new byte[] { 0x00, 0xC2, 0x80, 0xE1, 0x88, 0xB4 }), allowUnpairedSurrogates: false); writer.WriteUTF8("\0\ud800", allowUnpairedSurrogates: false); // hi surrogate writer.WriteUTF8("\0\udc00", allowUnpairedSurrogates: false); // lo surrogate writer.WriteUTF8("\0\ud800\udc00", allowUnpairedSurrogates: false); // pair writer.WriteUTF8("\0\udc00\ud800", allowUnpairedSurrogates: false); // lo + hi AssertEx.Equal(new byte[] { (byte)'a', (byte)'b', (byte)'c', (byte)'d', 0x00, 0xC2, 0x80, 0xE1, 0x88, 0xB4, 0x00, 0xEF, 0xBF, 0xBD, 0x00, 0xEF, 0xBF, 0xBD, 0x00, 0xF0, 0x90, 0x80, 0x80, 0x00, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD }, writer.ToArray()); }
// Copied from Roslyn: MetadataWriter.SerializeTupleElementNames internal static void SerializeTupleElementNames(BlobBuilder builder, ImmutableArray <string> names) { foreach (var name in names) { if (name != null) { builder.WriteUTF8(name); } builder.WriteByte(0); } }
private static int WritePdbChecksumData(BlobBuilder builder, string algorithmName, ImmutableArray <byte> checksum) { int start = builder.Count; // NUL-terminated algorithm name: builder.WriteUTF8(algorithmName, allowUnpairedSurrogates: true); builder.WriteByte(0); // checksum: builder.WriteBytes(checksum); return(builder.Count - start); }
private BlobHandle GetCustomAttributeValueFromString(string str) { if (str == null) { return(default(BlobHandle)); } var builder = new BlobBuilder(); builder.WriteBytes(new byte[] { 0x01, 0x00 }); // "prolog" builder.WriteUTF8(str); return(GetBlob(builder)); }
public void Link() { var builder1 = new BlobBuilder(16); builder1.WriteByte(1); var builder2 = new BlobBuilder(16); builder2.WriteByte(2); var builder3 = new BlobBuilder(16); builder3.WriteByte(3); var builder4 = new BlobBuilder(16); builder4.WriteByte(4); var builder5 = new BlobBuilder(16); builder5.WriteByte(5); builder2.LinkPrefix(builder1); AssertEx.Equal(new byte[] { 1, 2 }, builder2.ToArray()); Assert.Throws <InvalidOperationException>(() => builder1.ToArray()); Assert.Throws <InvalidOperationException>(() => builder2.LinkPrefix(builder1)); Assert.Throws <InvalidOperationException>(() => builder1.WriteByte(0xff)); Assert.Throws <InvalidOperationException>(() => builder1.WriteBytes(1, 10)); Assert.Throws <InvalidOperationException>(() => builder1.WriteBytes(new byte[] { 1 })); Assert.Throws <InvalidOperationException>(() => builder1.ReserveBytes(1)); Assert.Throws <InvalidOperationException>(() => builder1.GetBlobs()); Assert.Throws <InvalidOperationException>(() => builder1.ContentEquals(builder1)); Assert.Throws <InvalidOperationException>(() => builder1.WriteUTF16("str")); Assert.Throws <InvalidOperationException>(() => builder1.WriteUTF8("str", allowUnpairedSurrogates: false)); builder2.LinkSuffix(builder3); AssertEx.Equal(new byte[] { 1, 2, 3 }, builder2.ToArray()); Assert.Throws <InvalidOperationException>(() => builder3.LinkPrefix(builder5)); builder2.LinkPrefix(builder4); AssertEx.Equal(new byte[] { 4, 1, 2, 3 }, builder2.ToArray()); Assert.Throws <InvalidOperationException>(() => builder4.LinkPrefix(builder5)); builder2.LinkSuffix(builder5); AssertEx.Equal(new byte[] { 4, 1, 2, 3, 5 }, builder2.ToArray()); }
/// <summary> /// Fills in stringIndexMap with data from stringIndex and write to stringWriter. /// Releases stringIndex as the stringTable is sealed after this point. /// </summary> private static ImmutableArray <int> SerializeStringHeap( BlobBuilder heapBuilder, Dictionary <string, StringHandle> strings, int stringHeapStartOffset) { // Sort by suffix and remove stringIndex var sorted = new List <KeyValuePair <string, StringHandle> >(strings); sorted.Sort(SuffixSort.Instance); // Create VirtIdx to Idx map and add entry for empty string int totalCount = sorted.Count + 1; var stringVirtualIndexToHeapOffsetMap = ImmutableArray.CreateBuilder <int>(totalCount); stringVirtualIndexToHeapOffsetMap.Count = totalCount; stringVirtualIndexToHeapOffsetMap[0] = 0; heapBuilder.WriteByte(0); // Find strings that can be folded string prev = string.Empty; foreach (KeyValuePair <string, StringHandle> entry in sorted) { int position = stringHeapStartOffset + heapBuilder.Count; // It is important to use ordinal comparison otherwise we'll use the current culture! if (prev.EndsWith(entry.Key, StringComparison.Ordinal) && !BlobUtilities.IsLowSurrogateChar(entry.Key[0])) { // Map over the tail of prev string. Watch for null-terminator of prev string. stringVirtualIndexToHeapOffsetMap[entry.Value.GetWriterVirtualIndex()] = position - (BlobUtilities.GetUTF8ByteCount(entry.Key) + 1); } else { stringVirtualIndexToHeapOffsetMap[entry.Value.GetWriterVirtualIndex()] = position; heapBuilder.WriteUTF8(entry.Key, allowUnpairedSurrogates: false); heapBuilder.WriteByte(0); } prev = entry.Key; } return(stringVirtualIndexToHeapOffsetMap.MoveToImmutable()); }
private static int WriteCodeViewData(BlobBuilder builder, string pdbPath, Guid pdbGuid, int age) { int start = builder.Count; builder.WriteByte((byte)'R'); builder.WriteByte((byte)'S'); builder.WriteByte((byte)'D'); builder.WriteByte((byte)'S'); // PDB id: builder.WriteGuid(pdbGuid); // age builder.WriteInt32(age); // UTF-8 encoded zero-terminated path to PDB builder.WriteUTF8(pdbPath, allowUnpairedSurrogates: true); builder.WriteByte(0); return(builder.Count - start); }
/// <summary> /// Fills in stringIndexMap with data from stringIndex and write to stringWriter. /// Releases stringIndex as the stringTable is sealed after this point. /// </summary> private void SerializeStringHeap() { // Sort by suffix and remove stringIndex var sorted = new List <KeyValuePair <string, StringHandle> >(_strings); sorted.Sort(new SuffixSort()); _strings = null; _stringWriter = new BlobBuilder(1024); // Create VirtIdx to Idx map and add entry for empty string _stringIndexToResolvedOffsetMap = new int[sorted.Count + 1]; _stringIndexToResolvedOffsetMap[0] = 0; _stringWriter.WriteByte(0); // Find strings that can be folded string prev = string.Empty; foreach (KeyValuePair <string, StringHandle> entry in sorted) { int position = _stringHeapStartOffset + _stringWriter.Position; // It is important to use ordinal comparison otherwise we'll use the current culture! if (prev.EndsWith(entry.Key, StringComparison.Ordinal) && !BlobUtilities.IsLowSurrogateChar(entry.Key[0])) { // Map over the tail of prev string. Watch for null-terminator of prev string. _stringIndexToResolvedOffsetMap[MetadataTokens.GetHeapOffset(entry.Value)] = position - (BlobUtilities.GetUTF8ByteCount(entry.Key) + 1); } else { _stringIndexToResolvedOffsetMap[MetadataTokens.GetHeapOffset(entry.Value)] = position; _stringWriter.WriteUTF8(entry.Key, allowUnpairedSurrogates: false); _stringWriter.WriteByte(0); } prev = entry.Key; } }
public void WriteUTF8_Substring() { var writer = new BlobBuilder(4); writer.WriteUTF8("abc", 0, 0, allowUnpairedSurrogates: true, prependSize: false); AssertEx.Equal(new byte[0], writer.ToArray()); writer.Clear(); writer.WriteUTF8("abc", 0, 1, allowUnpairedSurrogates: true, prependSize: false); AssertEx.Equal(new[] { (byte)'a' }, writer.ToArray()); writer.Clear(); writer.WriteUTF8("abc", 0, 2, allowUnpairedSurrogates: true, prependSize: false); AssertEx.Equal(new[] { (byte)'a', (byte)'b' }, writer.ToArray()); writer.Clear(); writer.WriteUTF8("abc", 0, 3, allowUnpairedSurrogates: true, prependSize: false); AssertEx.Equal(new[] { (byte)'a', (byte)'b', (byte)'c' }, writer.ToArray()); writer.Clear(); writer.WriteUTF8("abc", 1, 0, allowUnpairedSurrogates: true, prependSize: false); AssertEx.Equal(new byte[0], writer.ToArray()); writer.Clear(); writer.WriteUTF8("abc", 1, 1, allowUnpairedSurrogates: true, prependSize: false); AssertEx.Equal(new[] { (byte)'b' }, writer.ToArray()); writer.Clear(); writer.WriteUTF8("abc", 1, 2, allowUnpairedSurrogates: true, prependSize: false); AssertEx.Equal(new[] { (byte)'b', (byte)'c' }, writer.ToArray()); writer.Clear(); writer.WriteUTF8("abc", 2, 1, allowUnpairedSurrogates: true, prependSize: false); AssertEx.Equal(new[] { (byte)'c' }, writer.ToArray()); writer.Clear(); }
/// <summary> /// Serialize the export symbol table into the export section. /// </summary> /// <param name="location">RVA and file location of the .edata section</param> private BlobBuilder SerializeExportSection(SectionLocation sectionLocation) { _exportSymbols.MergeSort((es1, es2) => StringComparer.Ordinal.Compare(es1.Name, es2.Name)); BlobBuilder builder = new BlobBuilder(); int minOrdinal = int.MaxValue; int maxOrdinal = int.MinValue; // First, emit the name table and store the name RVA's for the individual export symbols // Also, record the ordinal range. foreach (ExportSymbol symbol in _exportSymbols) { symbol.NameRVAWhenPlaced = sectionLocation.RelativeVirtualAddress + builder.Count; builder.WriteUTF8(symbol.Name); builder.WriteByte(0); if (symbol.Ordinal < minOrdinal) { minOrdinal = symbol.Ordinal; } if (symbol.Ordinal > maxOrdinal) { maxOrdinal = symbol.Ordinal; } } // Emit the DLL name int dllNameRVA = sectionLocation.RelativeVirtualAddress + builder.Count; builder.WriteUTF8(_dllNameForExportDirectoryTable); builder.WriteByte(0); int[] addressTable = new int[maxOrdinal - minOrdinal + 1]; // Emit the name pointer table; it should be alphabetically sorted. // Also, we can now fill in the export address table as we've detected its size // in the previous pass. int namePointerTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count; foreach (ExportSymbol symbol in _exportSymbols) { builder.WriteInt32(symbol.NameRVAWhenPlaced); SymbolTarget symbolTarget = _symbolMap[symbol.Symbol]; Section symbolSection = _sections[symbolTarget.SectionIndex]; Debug.Assert(symbolSection.RVAWhenPlaced != 0); addressTable[symbol.Ordinal - minOrdinal] = symbolSection.RVAWhenPlaced + symbolTarget.Offset; } // Emit the ordinal table int ordinalTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count; foreach (ExportSymbol symbol in _exportSymbols) { builder.WriteUInt16((ushort)(symbol.Ordinal - minOrdinal)); } // Emit the address table int addressTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count; foreach (int addressTableEntry in addressTable) { builder.WriteInt32(addressTableEntry); } // Emit the export directory table int exportDirectoryTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count; // +0x00: reserved builder.WriteInt32(0); // +0x04: TODO: time/date stamp builder.WriteInt32(0); // +0x08: major version builder.WriteInt16(0); // +0x0A: minor version builder.WriteInt16(0); // +0x0C: DLL name RVA builder.WriteInt32(dllNameRVA); // +0x10: ordinal base builder.WriteInt32(minOrdinal); // +0x14: number of entries in the address table builder.WriteInt32(addressTable.Length); // +0x18: number of name pointers builder.WriteInt32(_exportSymbols.Count); // +0x1C: export address table RVA builder.WriteInt32(addressTableRVA); // +0x20: name pointer RVV builder.WriteInt32(namePointerTableRVA); // +0x24: ordinal table RVA builder.WriteInt32(ordinalTableRVA); int exportDirectorySize = sectionLocation.RelativeVirtualAddress + builder.Count - exportDirectoryTableRVA; _exportDirectoryEntry = new DirectoryEntry(relativeVirtualAddress: exportDirectoryTableRVA, size: exportDirectorySize); return(builder); }
private readonly static byte[] zeroStamp = new byte[4]; // four bytes of zero /// <summary> /// Write the entire "Debug Directory (Image Only)" along with data that it points to. /// </summary> internal void WriteDebugTable(BlobBuilder builder, PESectionLocation textSectionLocation, ContentId nativePdbContentId, ContentId portablePdbContentId) { Debug.Assert(builder.Count == 0); int tableSize = ImageDebugDirectoryBaseSize; Debug.Assert(tableSize != 0); Debug.Assert(nativePdbContentId.IsDefault || portablePdbContentId.IsDefault); Debug.Assert(!EmitPdb || (nativePdbContentId.IsDefault ^ portablePdbContentId.IsDefault)); int dataSize = ComputeSizeOfDebugDirectoryData(); if (EmitPdb) { const int IMAGE_DEBUG_TYPE_CODEVIEW = 2; // from PE spec uint dataOffset = (uint)(ComputeOffsetToDebugTable() + tableSize); WriteDebugTableEntry(builder, stamp: nativePdbContentId.Stamp ?? portablePdbContentId.Stamp, version: portablePdbContentId.IsDefault ? (uint)0 : ('P' << 24 | 'M' << 16 | 0x01 << 8 | 0x00), debugType: IMAGE_DEBUG_TYPE_CODEVIEW, sizeOfData: (uint)dataSize, addressOfRawData: (uint)textSectionLocation.RelativeVirtualAddress + dataOffset, // RVA of the data pointerToRawData: (uint)textSectionLocation.PointerToRawData + dataOffset); // position of the data in the PE stream } if (IsDeterministic) { const int IMAGE_DEBUG_TYPE_NO_TIMESTAMP = 16; // from PE spec WriteDebugTableEntry(builder, stamp: zeroStamp, version: 0, debugType: IMAGE_DEBUG_TYPE_NO_TIMESTAMP, sizeOfData: 0, addressOfRawData: 0, pointerToRawData: 0); } // We should now have written all and precisely the data we said we'd write for the table entries. Debug.Assert(builder.Count == tableSize); // ==================== // The following is additional data beyond the debug directory at the offset `dataOffset` // pointed to by the ImageDebugTypeCodeView entry. if (EmitPdb) { builder.WriteByte((byte)'R'); builder.WriteByte((byte)'S'); builder.WriteByte((byte)'D'); builder.WriteByte((byte)'S'); // PDB id: builder.WriteBytes(nativePdbContentId.Guid ?? portablePdbContentId.Guid); // age builder.WriteUInt32(1); // TODO: allow specify for native PDBs // UTF-8 encoded zero-terminated path to PDB int pathStart = builder.Count; builder.WriteUTF8(PdbPathOpt, allowUnpairedSurrogates: true); builder.WriteByte(0); // padding: builder.WriteBytes(0, Math.Max(0, MinPdbPath - (builder.Count - pathStart))); } // We should now have written all and precisely the data we said we'd write for the table and its data. Debug.Assert(builder.Count == tableSize + dataSize); }
/// <summary> /// Write string as UTF8 with null terminator. /// </summary> private static void WriteUtf8String(BlobBuilder builder, string str) { builder.WriteUTF8(str); builder.WriteByte(0); }
public void Link() { var builder1 = new BlobBuilder(16); builder1.WriteByte(1); var builder2 = new BlobBuilder(16); builder2.WriteByte(2); var builder3 = new BlobBuilder(16); builder3.WriteByte(3); var builder4 = new BlobBuilder(16); builder4.WriteByte(4); var builder5 = new BlobBuilder(16); builder5.WriteByte(5); builder2.LinkPrefix(builder1); AssertEx.Equal(new byte[] { 1, 2 }, builder2.ToArray()); Assert.Throws<InvalidOperationException>(() => builder1.ToArray()); Assert.Throws<InvalidOperationException>(() => builder2.LinkPrefix(builder1)); Assert.Throws<InvalidOperationException>(() => builder1.WriteByte(0xff)); Assert.Throws<InvalidOperationException>(() => builder1.WriteBytes(1, 10)); Assert.Throws<InvalidOperationException>(() => builder1.WriteBytes(new byte[] { 1 })); Assert.Throws<InvalidOperationException>(() => builder1.ReserveBytes(1)); Assert.Throws<InvalidOperationException>(() => builder1.GetBlobs()); Assert.Throws<InvalidOperationException>(() => builder1.ContentEquals(builder1)); Assert.Throws<InvalidOperationException>(() => builder1.WriteUTF16("str")); Assert.Throws<InvalidOperationException>(() => builder1.WriteUTF8("str", allowUnpairedSurrogates: false)); builder2.LinkSuffix(builder3); AssertEx.Equal(new byte[] { 1, 2, 3 }, builder2.ToArray()); Assert.Throws<InvalidOperationException>(() => builder3.LinkPrefix(builder5)); builder2.LinkPrefix(builder4); AssertEx.Equal(new byte[] { 4, 1, 2, 3 }, builder2.ToArray()); Assert.Throws<InvalidOperationException>(() => builder4.LinkPrefix(builder5)); builder2.LinkSuffix(builder5); AssertEx.Equal(new byte[] { 4, 1, 2, 3, 5 }, builder2.ToArray()); }
public unsafe void Write_Errors() { var builder = new BlobBuilder(16); Assert.Throws<ArgumentNullException>(() => builder.WriteUTF16((char[])null)); Assert.Throws<ArgumentNullException>(() => builder.WriteUTF16((string)null)); Assert.Throws<ArgumentNullException>(() => builder.WriteUTF8(null, allowUnpairedSurrogates: true)); Assert.Throws<ArgumentNullException>(() => builder.WriteUTF8(null, allowUnpairedSurrogates: true)); Assert.Throws<ArgumentNullException>(() => builder.TryWriteBytes((Stream)null, 0)); Assert.Throws<ArgumentNullException>(() => builder.WriteBytes(null)); Assert.Throws<ArgumentNullException>(() => builder.WriteBytes(null, 0, 0)); Assert.Throws<ArgumentNullException>(() => builder.WriteBytes((byte*)null, 0)); Assert.Throws<ArgumentNullException>(() => builder.WriteBytes(default(ImmutableArray<byte>))); Assert.Throws<ArgumentNullException>(() => builder.WriteBytes(default(ImmutableArray<byte>), 0, 0)); var bw = default(BlobWriter); Assert.Throws<ArgumentNullException>(() => builder.WriteContentTo(ref bw)); Assert.Throws<ArgumentNullException>(() => builder.WriteContentTo((Stream)null)); Assert.Throws<ArgumentNullException>(() => builder.WriteContentTo((BlobBuilder)null)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.TryWriteBytes(new MemoryStream(), -1)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(0, -1)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(new byte[] { }, 1, 0)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(new byte[] { }, 0, 1)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(new byte[] { }, 0, -1)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(ImmutableArray<byte>.Empty, 1, 0)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(ImmutableArray<byte>.Empty, 0, 1)); Assert.Throws<ArgumentOutOfRangeException>(() => builder.WriteBytes(ImmutableArray<byte>.Empty, 1, -1)); }
internal static void SerializeMetadataHeader(BlobBuilder builder, string metadataVersion, MetadataSizes sizes) { int startOffset = builder.Count; // signature builder.WriteUInt32(0x424A5342); // major version builder.WriteUInt16(1); // minor version builder.WriteUInt16(1); // reserved builder.WriteUInt32(0); // Spec (section 24.2.1 Metadata Root): // Length ... Number of bytes allocated to hold version string (including null terminator), call this x. // Call the length of the string (including the terminator) m (we require m <= 255); // the length x is m rounded up to a multiple of four. builder.WriteInt32(sizes.MetadataVersionPaddedLength); int metadataVersionStart = builder.Count; builder.WriteUTF8(metadataVersion); builder.WriteByte(0); int metadataVersionEnd = builder.Count; for (int i = 0; i < sizes.MetadataVersionPaddedLength - (metadataVersionEnd - metadataVersionStart); i++) { builder.WriteByte(0); } // reserved builder.WriteUInt16(0); // number of streams builder.WriteUInt16((ushort)(5 + (sizes.IsEncDelta ? 1 : 0) + (sizes.IsStandaloneDebugMetadata ? 1 : 0))); // stream headers int offsetFromStartOfMetadata = sizes.MetadataHeaderSize; // emit the #Pdb stream first so that only a single page has to be read in order to find out PDB ID if (sizes.IsStandaloneDebugMetadata) { SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.StandalonePdbStreamSize, "#Pdb", builder); } // Spec: Some compilers store metadata in a #- stream, which holds an uncompressed, or non-optimized, representation of metadata tables; // this includes extra metadata -Ptr tables. Such PE files do not form part of ECMA-335 standard. // // Note: EnC delta is stored as uncompressed metadata stream. SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.MetadataTableStreamSize, (sizes.IsCompressed ? "#~" : "#-"), builder); SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.String), "#Strings", builder); SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.UserString), "#US", builder); SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.Guid), "#GUID", builder); SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.Blob), "#Blob", builder); if (sizes.IsEncDelta) { SerializeStreamHeader(ref offsetFromStartOfMetadata, 0, "#JTD", builder); } int endOffset = builder.Count; Debug.Assert(endOffset - startOffset == sizes.MetadataHeaderSize); }