private static void WriteFakeILWithBranches(BlobBuilder builder, ControlFlowBuilder branchBuilder, int size) { Assert.Equal(0, builder.Count); const byte filling = 0x01; int ilOffset = 0; foreach (var branch in branchBuilder.Branches) { builder.WriteBytes(filling, branch.ILOffset - ilOffset); Assert.Equal(branch.ILOffset, builder.Count); builder.WriteByte((byte)branch.OpCode); int operandSize = branch.OpCode.GetBranchOperandSize(); if (operandSize == 1) { builder.WriteSByte(-1); } else { builder.WriteInt32(-1); } ilOffset = branch.ILOffset + sizeof(byte) + operandSize; } builder.WriteBytes(filling, size - ilOffset); Assert.Equal(size, builder.Count); }
private static void WriteFakeILWithBranches(BlobBuilder builder, BranchBuilder branchBuilder, int size) { Assert.Equal(0, builder.Count); const byte filling = 0x01; int ilOffset = 0; foreach (var branch in branchBuilder.Branches) { builder.WriteBytes(filling, branch.ILOffset - ilOffset); Assert.Equal(branch.ILOffset, builder.Count); builder.WriteByte(branch.ShortOpCode); builder.WriteByte(0xff); ilOffset = branch.ILOffset + 2; } builder.WriteBytes(filling, size - ilOffset); Assert.Equal(size, builder.Count); }
public void OpCode() { var builder = new BlobBuilder(); builder.WriteByte(0xff); var il = new InstructionEncoder(builder); Assert.Equal(1, il.Offset); il.OpCode(ILOpCode.Add); Assert.Equal(2, il.Offset); builder.WriteByte(0xee); Assert.Equal(3, il.Offset); il.OpCode(ILOpCode.Arglist); Assert.Equal(5, il.Offset); builder.WriteByte(0xdd); Assert.Equal(6, il.Offset); il.OpCode(ILOpCode.Readonly); Assert.Equal(8, il.Offset); builder.WriteByte(0xcc); Assert.Equal(9, il.Offset); AssertEx.Equal(new byte[] { 0xFF, 0x58, 0xEE, 0xFE, 0x00, 0xDD, 0xFE, 0x1E, 0xCC }, builder.ToArray()); }
public void SerializeTableHeader() { var builder = new BlobBuilder(); builder.WriteByte(0xff); ExceptionRegionEncoder.SerializeTableHeader(builder, ExceptionRegionEncoder.MaxSmallExceptionRegions, hasSmallRegions: true); AssertEx.Equal(new byte[] { 0xff, 0x00, 0x00, 0x00, // padding 0x01, // flags 0xf4, // size 0x00, 0x00 }, builder.ToArray()); builder.Clear(); builder.WriteByte(0xff); ExceptionRegionEncoder.SerializeTableHeader(builder, ExceptionRegionEncoder.MaxExceptionRegions, hasSmallRegions: false); AssertEx.Equal(new byte[] { 0xff, 0x00, 0x00, 0x00, // padding 0x41, // flags 0xf4, 0xff, 0xff, // size }, builder.ToArray()); }
public void CountClear() { var builder = new BlobBuilder(); Assert.Equal(0, builder.Count); builder.WriteByte(1); Assert.Equal(1, builder.Count); builder.WriteInt32(4); Assert.Equal(5, builder.Count); builder.Clear(); Assert.Equal(0, builder.Count); builder.WriteInt64(1); Assert.Equal(8, builder.Count); AssertEx.Equal(new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, builder.ToArray()); }
internal static void WriteCompressedInteger(BlobBuilder writer, int value) { unchecked { if (value <= SingleByteCompressedIntegerMaxValue) { writer.WriteByte((byte)value); } else if (value <= TwoByteCompressedIntegerMaxValue) { writer.WriteUInt16BE((ushort)(0x8000 | value)); } else if (value <= MaxCompressedIntegerValue) { writer.WriteUInt32BE(0xc0000000 | (uint)value); } else { ThrowValueArgumentOutOfRange(); } } }
/// <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. builder.Align(4); 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 builder.Align(4); int addressTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count; foreach (int addressTableEntry in addressTable) { builder.WriteInt32(addressTableEntry); } // Emit the export directory table builder.Align(4); 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 void WritePEHeader(BlobBuilder builder, PEDirectoriesBuilder headers, ImmutableArray<SerializedSection> sections) { builder.WriteUInt16((ushort)(Is32Bit ? PEMagic.PE32 : PEMagic.PE32Plus)); builder.WriteByte(MajorLinkerVersion); builder.WriteByte(MinorLinkerVersion); // SizeOfCode: builder.WriteUInt32((uint)SumRawDataSizes(sections, SectionCharacteristics.ContainsCode)); // SizeOfInitializedData: builder.WriteUInt32((uint)SumRawDataSizes(sections, SectionCharacteristics.ContainsInitializedData)); // SizeOfUninitializedData: builder.WriteUInt32((uint)SumRawDataSizes(sections, SectionCharacteristics.ContainsUninitializedData)); // AddressOfEntryPoint: builder.WriteUInt32((uint)headers.AddressOfEntryPoint); // BaseOfCode: int codeSectionIndex = IndexOfSection(sections, SectionCharacteristics.ContainsCode); builder.WriteUInt32((uint)(codeSectionIndex != -1 ? sections[codeSectionIndex].RelativeVirtualAddress : 0)); if (Is32Bit) { // BaseOfData: int dataSectionIndex = IndexOfSection(sections, SectionCharacteristics.ContainsInitializedData); builder.WriteUInt32((uint)(dataSectionIndex != -1 ? sections[dataSectionIndex].RelativeVirtualAddress : 0)); builder.WriteUInt32((uint)ImageBase); } else { builder.WriteUInt64(ImageBase); } // NT additional fields: builder.WriteUInt32((uint)SectionAlignment); builder.WriteUInt32((uint)FileAlignment); builder.WriteUInt16(MajorOperatingSystemVersion); builder.WriteUInt16(MinorOperatingSystemVersion); builder.WriteUInt16(MajorImageVersion); builder.WriteUInt16(MinorImageVersion); builder.WriteUInt16(MajorSubsystemVersion); builder.WriteUInt16(MinorSubsystemVersion); // Win32VersionValue (reserved, should be 0) builder.WriteUInt32(0); // SizeOfImage: var lastSection = sections[sections.Length - 1]; builder.WriteUInt32((uint)BitArithmeticUtilities.Align(lastSection.RelativeVirtualAddress + lastSection.VirtualSize, SectionAlignment)); // SizeOfHeaders: builder.WriteUInt32((uint)BitArithmeticUtilities.Align(ComputeSizeOfPeHeaders(sections.Length, Is32Bit), FileAlignment)); // Checksum (TODO: not supported): builder.WriteUInt32(0); builder.WriteUInt16((ushort)Subsystem); builder.WriteUInt16((ushort)DllCharacteristics); if (Is32Bit) { builder.WriteUInt32((uint)SizeOfStackReserve); builder.WriteUInt32((uint)SizeOfStackCommit); builder.WriteUInt32((uint)SizeOfHeapReserve); builder.WriteUInt32((uint)SizeOfHeapCommit); } else { builder.WriteUInt64(SizeOfStackReserve); builder.WriteUInt64(SizeOfStackCommit); builder.WriteUInt64(SizeOfHeapReserve); builder.WriteUInt64(SizeOfHeapCommit); } // LoaderFlags builder.WriteUInt32(0); // The number of data-directory entries in the remainder of the header. builder.WriteUInt32(16); // directory entries: builder.WriteUInt32((uint)headers.ExportTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.ExportTable.Size); builder.WriteUInt32((uint)headers.ImportTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.ImportTable.Size); builder.WriteUInt32((uint)headers.ResourceTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.ResourceTable.Size); builder.WriteUInt32((uint)headers.ExceptionTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.ExceptionTable.Size); builder.WriteUInt32((uint)headers.CertificateTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.CertificateTable.Size); builder.WriteUInt32((uint)headers.BaseRelocationTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.BaseRelocationTable.Size); builder.WriteUInt32((uint)headers.DebugTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.DebugTable.Size); builder.WriteUInt32((uint)headers.CopyrightTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.CopyrightTable.Size); builder.WriteUInt32((uint)headers.GlobalPointerTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.GlobalPointerTable.Size); builder.WriteUInt32((uint)headers.ThreadLocalStorageTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.ThreadLocalStorageTable.Size); builder.WriteUInt32((uint)headers.LoadConfigTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.LoadConfigTable.Size); builder.WriteUInt32((uint)headers.BoundImportTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.BoundImportTable.Size); builder.WriteUInt32((uint)headers.ImportAddressTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.ImportAddressTable.Size); builder.WriteUInt32((uint)headers.DelayImportTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.DelayImportTable.Size); builder.WriteUInt32((uint)headers.CorHeaderTable.RelativeVirtualAddress); builder.WriteUInt32((uint)headers.CorHeaderTable.Size); // Reserved, should be 0 builder.WriteUInt64(0); }
private BlobHandle SerializeLocalConstantSignature(ILocalDefinition localConstant) { var builder = new BlobBuilder(); // TODO: BlobEncoder.LocalConstantSignature // CustomMod* var encoder = new CustomModifiersEncoder(builder); SerializeCustomModifiers(encoder, localConstant.CustomModifiers); var type = localConstant.Type; var typeCode = type.TypeCode; object value = localConstant.CompileTimeValue.Value; // PrimitiveConstant or EnumConstant if (value is decimal) { builder.WriteByte((byte)SignatureTypeKind.ValueType); builder.WriteCompressedInteger(CodedIndex.TypeDefOrRefOrSpec(GetTypeHandle(type))); builder.WriteDecimal((decimal)value); } else if (value is DateTime) { builder.WriteByte((byte)SignatureTypeKind.ValueType); builder.WriteCompressedInteger(CodedIndex.TypeDefOrRefOrSpec(GetTypeHandle(type))); builder.WriteDateTime((DateTime)value); } else if (typeCode == PrimitiveTypeCode.String) { builder.WriteByte((byte)ConstantTypeCode.String); if (value == null) { builder.WriteByte(0xff); } else { builder.WriteUTF16((string)value); } } else if (value != null) { // TypeCode builder.WriteByte((byte)GetConstantTypeCode(value)); // Value builder.WriteConstant(value); // EnumType if (type.IsEnum) { builder.WriteCompressedInteger(CodedIndex.TypeDefOrRefOrSpec(GetTypeHandle(type))); } } else if (this.module.IsPlatformType(type, PlatformType.SystemObject)) { builder.WriteByte((byte)SignatureTypeCode.Object); } else { builder.WriteByte((byte)(type.IsValueType ? SignatureTypeKind.ValueType : SignatureTypeKind.Class)); builder.WriteCompressedInteger(CodedIndex.TypeDefOrRefOrSpec(GetTypeHandle(type))); } return(_debugMetadataOpt.GetOrAddBlob(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); }
public static void SerializeWin32Resources(BlobBuilder builder, IEnumerable<IWin32Resource> theResources, int resourcesRva) { theResources = SortResources(theResources); Directory typeDirectory = new Directory(string.Empty, 0); Directory nameDirectory = null; Directory languageDirectory = null; int lastTypeID = int.MinValue; string lastTypeName = null; int lastID = int.MinValue; string lastName = null; uint sizeOfDirectoryTree = 16; //EDMAURER note that this list is assumed to be sorted lowest to highest //first by typeId, then by Id. foreach (IWin32Resource r in theResources) { bool typeDifferent = (r.TypeId < 0 && r.TypeName != lastTypeName) || r.TypeId > lastTypeID; if (typeDifferent) { lastTypeID = r.TypeId; lastTypeName = r.TypeName; if (lastTypeID < 0) { Debug.Assert(typeDirectory.NumberOfIdEntries == 0, "Not all Win32 resources with types encoded as strings precede those encoded as ints"); typeDirectory.NumberOfNamedEntries++; } else { typeDirectory.NumberOfIdEntries++; } sizeOfDirectoryTree += 24; typeDirectory.Entries.Add(nameDirectory = new Directory(lastTypeName, lastTypeID)); } if (typeDifferent || (r.Id < 0 && r.Name != lastName) || r.Id > lastID) { lastID = r.Id; lastName = r.Name; if (lastID < 0) { Debug.Assert(nameDirectory.NumberOfIdEntries == 0, "Not all Win32 resources with names encoded as strings precede those encoded as ints"); nameDirectory.NumberOfNamedEntries++; } else { nameDirectory.NumberOfIdEntries++; } sizeOfDirectoryTree += 24; nameDirectory.Entries.Add(languageDirectory = new Directory(lastName, lastID)); } languageDirectory.NumberOfIdEntries++; sizeOfDirectoryTree += 8; languageDirectory.Entries.Add(r); } var dataWriter = new BlobBuilder(); //'dataWriter' is where opaque resource data goes as well as strings that are used as type or name identifiers WriteDirectory(typeDirectory, builder, 0, 0, sizeOfDirectoryTree, resourcesRva, dataWriter); builder.LinkSuffix(dataWriter); builder.WriteByte(0); builder.Align(4); }
private BlobHandle SerializeDocumentName(string name) { Debug.Assert(name != null); int c1 = Count(name, s_separator1[0]); int c2 = Count(name, s_separator2[0]); char[] separator = (c1 >= c2) ? s_separator1 : s_separator2; // Estimate 2 bytes per part, if the blob heap gets big we expand the builder once. var writer = new BlobBuilder(1 + Math.Max(c1, c2) * 2); writer.WriteByte((byte)separator[0]); // TODO: avoid allocations foreach (var part in name.Split(separator)) { BlobHandle partIndex = GetOrAddBlob(ImmutableArray.Create(MetadataWriter.s_utf8Encoding.GetBytes(part))); writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(partIndex)); } return GetOrAddBlob(writer); }
private void WriteImportTable(BlobBuilder builder, int importTableRva, int importAddressTableRva) { int start = builder.Count; int ilRVA = importTableRva + 40; int hintRva = ilRVA + (Is32Bit ? 12 : 16); int nameRva = hintRva + 12 + 2; // Import table builder.WriteUInt32((uint)ilRVA); // 4 builder.WriteUInt32(0); // 8 builder.WriteUInt32(0); // 12 builder.WriteUInt32((uint)nameRva); // 16 builder.WriteUInt32((uint)importAddressTableRva); // 20 builder.WriteBytes(0, 20); // 40 // Import Lookup table if (Is32Bit) { builder.WriteUInt32((uint)hintRva); // 44 builder.WriteUInt32(0); // 48 builder.WriteUInt32(0); // 52 } else { builder.WriteUInt64((uint)hintRva); // 48 builder.WriteUInt64(0); // 56 } // Hint table builder.WriteUInt16(0); // Hint 54|58 foreach (char ch in CorEntryPointName) { builder.WriteByte((byte)ch); // 65|69 } builder.WriteByte(0); // 66|70 Debug.Assert(builder.Count - start == SizeOfImportTable); }
public void WritePrimitive() { var writer = new BlobBuilder(17); writer.WriteUInt32(0x11223344); writer.WriteUInt16(0x5566); writer.WriteByte(0x77); writer.WriteUInt64(0x8899aabbccddeeff); writer.WriteInt32(-1); writer.WriteInt16(-2); writer.WriteSByte(-3); writer.WriteBoolean(true); writer.WriteBoolean(false); writer.WriteInt64(unchecked((long)0xfedcba0987654321)); writer.WriteDateTime(new DateTime(0x1112223334445556)); writer.WriteDecimal(102030405060.70m); writer.WriteDouble(double.NaN); writer.WriteSingle(float.NegativeInfinity); var guid = new Guid("01020304-0506-0708-090A-0B0C0D0E0F10"); writer.WriteBytes(guid.ToByteArray()); writer.WriteGuid(guid); AssertEx.Equal(new byte[] { 0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x77, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfd, 0x01, 0x00, 0x21, 0x43, 0x65, 0x87, 0x09, 0xBA, 0xDC, 0xFE, 0x56, 0x55, 0x44, 0x34, 0x33, 0x22, 0x12, 0x11, 0x02, 0xD6, 0xE0, 0x9A, 0x94, 0x47, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x80, 0xFF, 0x04, 0x03, 0x02, 0x01, 0x06, 0x05, 0x08, 0x07, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x04, 0x03, 0x02, 0x01, 0x06, 0x05, 0x08, 0x07, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 }, writer.ToArray()); }
internal static void WritePrimitiveType(BlobBuilder builder, PrimitiveTypeCode type) { switch (type) { case PrimitiveTypeCode.Boolean: case PrimitiveTypeCode.Byte: case PrimitiveTypeCode.SByte: case PrimitiveTypeCode.Char: case PrimitiveTypeCode.Int16: case PrimitiveTypeCode.UInt16: case PrimitiveTypeCode.Int32: case PrimitiveTypeCode.UInt32: case PrimitiveTypeCode.Int64: case PrimitiveTypeCode.UInt64: case PrimitiveTypeCode.Single: case PrimitiveTypeCode.Double: case PrimitiveTypeCode.IntPtr: case PrimitiveTypeCode.UIntPtr: case PrimitiveTypeCode.String: case PrimitiveTypeCode.Object: builder.WriteByte((byte)type); return; // TODO: should we allow these? case PrimitiveTypeCode.TypedReference: case PrimitiveTypeCode.Void: default: Throw.ArgumentOutOfRange(nameof(type)); return; } }
public static void Write( BlobBuilder writer, IReadOnlyList <Instruction> il, Func <string, StringHandle> getString, IReadOnlyDictionary <Guid, EntityHandle> typeHandles, IReadOnlyDictionary <ConstructorInfo, MemberReferenceHandle> ctorRefHandles, IReadOnlyDictionary <FieldInfo, FieldDefinitionHandle> fieldHandles, IReadOnlyDictionary <MethodInfo, MethodDefinitionHandle> methodHandles) { var targetOffsets = new ArrayMapper <int>(); //var offsetIndex = new Dictionary<int, int>(); for (var i = 0; i < il.Count; i++) { //offsetIndex.Add(il[i].Offset, i); var opCode = il[i].OpCode; opCode.WriteOpCode(writer.WriteByte); switch (opCode.OperandType) { case OperandType.InlineNone: break; case OperandType.InlineSwitch: var branches = (int[])il[i].Operand; writer.WriteInt32(branches.Length); for (var k = 0; k < branches.Length; k++) { var branchOffset = branches[k]; writer.WriteInt32(targetOffsets.Add( branchOffset + il[i].Offset + opCode.Size + 4 * (branches.Length + 1))); } break; case OperandType.ShortInlineBrTarget: var offset8 = (sbyte)il[i].Operand; // offset convention in IL: zero is at next instruction writer.WriteSByte((sbyte)targetOffsets.Add(offset8 + il[i].Offset + opCode.Size + 1)); break; case OperandType.InlineBrTarget: var offset32 = (int)il[i].Operand; // offset convention in IL: zero is at next instruction writer.WriteInt32(targetOffsets.Add(offset32 + il[i].Offset + opCode.Size + 4)); break; case OperandType.ShortInlineI: if (opCode == OpCodes.Ldc_I4_S) { writer.WriteSByte((sbyte)il[i].Operand); } else { writer.WriteByte((byte)il[i].Operand); } break; case OperandType.InlineI: writer.WriteInt32((int)il[i].Operand); break; case OperandType.ShortInlineR: writer.WriteSingle((float)il[i].Operand); break; case OperandType.InlineR: writer.WriteDouble((double)il[i].Operand); break; case OperandType.InlineI8: writer.WriteInt64((long)il[i].Operand); break; case OperandType.InlineSig: writer.WriteBytes((byte[])il[i].Operand); break; case OperandType.InlineString: writer.WriteInt32(MetadataTokens.GetToken(getString((string)il[i].Operand))); break; case OperandType.InlineType: case OperandType.InlineTok: case OperandType.InlineMethod: case OperandType.InlineField: switch (il[i].Operand) { case Type type: writer.WriteInt32(MetadataTokens.GetToken(typeHandles[type.GUID])); break; case ConstructorInfo constructorInfo: writer.WriteInt32(MetadataTokens.GetToken(ctorRefHandles[constructorInfo])); break; case FieldInfo fieldInfo: writer.WriteInt32(MetadataTokens.GetToken(fieldHandles[fieldInfo])); break; case MethodInfo methodInfo: writer.WriteInt32(MetadataTokens.GetToken(methodHandles[methodInfo])); break; default: throw new NotSupportedException(); } break; case OperandType.ShortInlineVar: var bLocalVariableInfo = il[i].Operand as LocalVariableInfo; var bParameterInfo = il[i].Operand as ParameterInfo; if (bLocalVariableInfo != null) { writer.WriteByte((byte)bLocalVariableInfo.LocalIndex); } else if (bParameterInfo != null) { writer.WriteByte((byte)bParameterInfo.Position); } else { throw new NotSupportedException(); } break; case OperandType.InlineVar: var sLocalVariableInfo = il[i].Operand as LocalVariableInfo; var sParameterInfo = il[i].Operand as ParameterInfo; if (sLocalVariableInfo != null) { writer.WriteUInt16((ushort)sLocalVariableInfo.LocalIndex); } else if (sParameterInfo != null) { writer.WriteUInt16((ushort)sParameterInfo.Position); } else { throw new NotSupportedException(); } break; default: throw new NotSupportedException(); } } }
private void WriteDirectory(Directory directory, BlobBuilder writer, uint offset, uint level, uint sizeOfDirectoryTree, int virtualAddressBase, BlobBuilder dataWriter) { writer.WriteUInt32(0); // Characteristics writer.WriteUInt32(0); // Timestamp writer.WriteUInt32(0); // Version writer.WriteUInt16(directory.NumberOfNamedEntries); writer.WriteUInt16(directory.NumberOfIdEntries); uint n = (uint)directory.Entries.Count; uint k = offset + 16 + n * 8; for (int i = 0; i < n; i++) { int id; string name; uint nameOffset = (uint)dataWriter.Position + sizeOfDirectoryTree; uint directoryOffset = k; Directory subDir = directory.Entries[i] as Directory; if (subDir != null) { id = subDir.ID; name = subDir.Name; if (level == 0) { k += SizeOfDirectory(subDir); } else { k += 16 + 8 * (uint)subDir.Entries.Count; } } else { //EDMAURER write out an IMAGE_RESOURCE_DATA_ENTRY followed //immediately by the data that it refers to. This results //in a layout different than that produced by pulling the resources //from an OBJ. In that case all of the data bits of a resource are //contiguous in .rsrc$02. After processing these will end up at //the end of .rsrc following all of the directory //info and IMAGE_RESOURCE_DATA_ENTRYs IWin32Resource r = (IWin32Resource)directory.Entries[i]; id = level == 0 ? r.TypeId : level == 1 ? r.Id : (int)r.LanguageId; name = level == 0 ? r.TypeName : level == 1 ? r.Name : null; dataWriter.WriteUInt32((uint)(virtualAddressBase + sizeOfDirectoryTree + 16 + dataWriter.Position)); byte[] data = new List<byte>(r.Data).ToArray(); dataWriter.WriteUInt32((uint)data.Length); dataWriter.WriteUInt32(r.CodePage); dataWriter.WriteUInt32(0); dataWriter.WriteBytes(data); while ((dataWriter.Count % 4) != 0) { dataWriter.WriteByte(0); } } if (id >= 0) { writer.WriteInt32(id); } else { if (name == null) { name = string.Empty; } writer.WriteUInt32(nameOffset | 0x80000000); dataWriter.WriteUInt16((ushort)name.Length); dataWriter.WriteUTF16(name); } if (subDir != null) { writer.WriteUInt32(directoryOffset | 0x80000000); } else { writer.WriteUInt32(nameOffset); } } k = offset + 16 + n * 8; for (int i = 0; i < n; i++) { Directory subDir = directory.Entries[i] as Directory; if (subDir != null) { this.WriteDirectory(subDir, writer, k, level + 1, sizeOfDirectoryTree, virtualAddressBase, dataWriter); if (level == 0) { k += SizeOfDirectory(subDir); } else { k += 16 + 8 * (uint)subDir.Entries.Count; } } } }
internal static void WriteConstant(BlobBuilder writer, object value) { if (value == null) { // The encoding of Type for the nullref value for FieldInit is ELEMENT_TYPE_CLASS with a Value of a 32-bit. writer.WriteUInt32(0); return; } var type = value.GetType(); if (type.GetTypeInfo().IsEnum) { type = Enum.GetUnderlyingType(type); } if (type == typeof(bool)) { writer.WriteBoolean((bool)value); } else if (type == typeof(int)) { writer.WriteInt32((int)value); } else if (type == typeof(string)) { writer.WriteUTF16((string)value); } else if (type == typeof(byte)) { writer.WriteByte((byte)value); } else if (type == typeof(char)) { writer.WriteUInt16((char)value); } else if (type == typeof(double)) { writer.WriteDouble((double)value); } else if (type == typeof(short)) { writer.WriteInt16((short)value); } else if (type == typeof(long)) { writer.WriteInt64((long)value); } else if (type == typeof(sbyte)) { writer.WriteSByte((sbyte)value); } else if (type == typeof(float)) { writer.WriteSingle((float)value); } else if (type == typeof(ushort)) { writer.WriteUInt16((ushort)value); } else if (type == typeof(uint)) { writer.WriteUInt32((uint)value); } else if (type == typeof(ulong)) { writer.WriteUInt64((ulong)value); } else { // TODO: message throw new ArgumentException(); } }
/// <exception cref="InvalidOperationException" /> internal void CopyCodeAndFixupBranches(BlobBuilder srcBuilder, BlobBuilder dstBuilder) { var branch = _branches[0]; int branchIndex = 0; // offset within the source builder int srcOffset = 0; // current offset within the current source blob int srcBlobOffset = 0; foreach (Blob srcBlob in srcBuilder.GetBlobs()) { Debug.Assert( srcBlobOffset == 0 || srcBlobOffset == 1 && srcBlob.Buffer[0] == 0xff || srcBlobOffset == 4 && srcBlob.Buffer[0] == 0xff && srcBlob.Buffer[1] == 0xff && srcBlob.Buffer[2] == 0xff && srcBlob.Buffer[3] == 0xff); while (true) { // copy bytes preceding the next branch, or till the end of the blob: int chunkSize = Math.Min(branch.ILOffset - srcOffset, srcBlob.Length - srcBlobOffset); dstBuilder.WriteBytes(srcBlob.Buffer, srcBlobOffset, chunkSize); srcOffset += chunkSize; srcBlobOffset += chunkSize; // there is no branch left in the blob: if (srcBlobOffset == srcBlob.Length) { srcBlobOffset = 0; break; } Debug.Assert(srcBlob.Buffer[srcBlobOffset] == (byte)branch.OpCode); int operandSize = branch.OpCode.GetBranchOperandSize(); bool isShortInstruction = operandSize == 1; // Note: the 4B operand is contiguous since we wrote it via BlobBuilder.WriteInt32() Debug.Assert( srcBlobOffset + 1 == srcBlob.Length || (isShortInstruction ? srcBlob.Buffer[srcBlobOffset + 1] == 0xff : BitConverter.ToUInt32(srcBlob.Buffer, srcBlobOffset + 1) == 0xffffffff)); // write branch opcode: dstBuilder.WriteByte(srcBlob.Buffer[srcBlobOffset]); int branchDistance = branch.GetBranchDistance(_labels, branch.OpCode, srcOffset, isShortInstruction); // write branch operand: if (isShortInstruction) { dstBuilder.WriteSByte((sbyte)branchDistance); } else { dstBuilder.WriteInt32(branchDistance); } srcOffset += sizeof(byte) + operandSize; // next branch: branchIndex++; if (branchIndex == _branches.Count) { branch = new BranchInfo(int.MaxValue, label: default, opCode: default);
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); }
/// <summary> /// Write string as UTF8 with null terminator. /// </summary> private static void WriteUtf8String(BlobBuilder builder, string str) { builder.WriteUTF8(str); builder.WriteByte(0); }
private void SerializeImport(BlobBuilder writer, UsedNamespaceOrType import) { if (import.TargetXmlNamespaceOpt != null) { Debug.Assert(import.TargetNamespaceOpt == null); Debug.Assert(import.TargetAssemblyOpt == null); Debug.Assert(import.TargetTypeOpt == null); // <import> ::= ImportXmlNamespace <alias> <target-namespace> writer.WriteByte((byte)ImportDefinitionKind.ImportXmlNamespace); writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(_debugMetadataOpt.GetOrAddBlobUTF8(import.AliasOpt))); writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(_debugMetadataOpt.GetOrAddBlobUTF8(import.TargetXmlNamespaceOpt))); } else if (import.TargetTypeOpt != null) { Debug.Assert(import.TargetNamespaceOpt == null); Debug.Assert(import.TargetAssemblyOpt == null); if (import.AliasOpt != null) { // <import> ::= AliasType <alias> <target-type> writer.WriteByte((byte)ImportDefinitionKind.AliasType); writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(_debugMetadataOpt.GetOrAddBlobUTF8(import.AliasOpt))); } else { // <import> ::= ImportType <target-type> writer.WriteByte((byte)ImportDefinitionKind.ImportType); } writer.WriteCompressedInteger(CodedIndex.TypeDefOrRefOrSpec(GetTypeHandle(import.TargetTypeOpt))); // TODO: index in release build } else if (import.TargetNamespaceOpt != null) { if (import.TargetAssemblyOpt != null) { if (import.AliasOpt != null) { // <import> ::= AliasAssemblyNamespace <alias> <target-assembly> <target-namespace> writer.WriteByte((byte)ImportDefinitionKind.AliasAssemblyNamespace); writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(_debugMetadataOpt.GetOrAddBlobUTF8(import.AliasOpt))); } else { // <import> ::= ImportAssemblyNamespace <target-assembly> <target-namespace> writer.WriteByte((byte)ImportDefinitionKind.ImportAssemblyNamespace); } writer.WriteCompressedInteger(MetadataTokens.GetRowNumber(GetAssemblyReferenceHandle(import.TargetAssemblyOpt))); } else { if (import.AliasOpt != null) { // <import> ::= AliasNamespace <alias> <target-namespace> writer.WriteByte((byte)ImportDefinitionKind.AliasNamespace); writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(_debugMetadataOpt.GetOrAddBlobUTF8(import.AliasOpt))); } else { // <import> ::= ImportNamespace <target-namespace> writer.WriteByte((byte)ImportDefinitionKind.ImportNamespace); } } // TODO: cache? string namespaceName = TypeNameSerializer.BuildQualifiedNamespaceName(import.TargetNamespaceOpt); writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(_debugMetadataOpt.GetOrAddBlobUTF8(namespaceName))); } else { // <import> ::= ImportReferenceAlias <alias> Debug.Assert(import.AliasOpt != null); Debug.Assert(import.TargetAssemblyOpt == null); writer.WriteByte((byte)ImportDefinitionKind.ImportAssemblyReferenceAlias); writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(_debugMetadataOpt.GetOrAddBlobUTF8(import.AliasOpt))); } }
public override void WriteByte(byte value) { _builder.WriteByte(value); }
private void WritePEHeader(BlobBuilder builder, PEDirectoriesBuilder directories, ImmutableArray <SerializedSection> sections) { builder.WriteUInt16((ushort)(Header.Is32Bit ? PEMagic.PE32 : PEMagic.PE32Plus)); builder.WriteByte(Header.MajorLinkerVersion); builder.WriteByte(Header.MinorLinkerVersion); // SizeOfCode: builder.WriteUInt32((uint)SumRawDataSizes(sections, SectionCharacteristics.ContainsCode)); // SizeOfInitializedData: builder.WriteUInt32((uint)SumRawDataSizes(sections, SectionCharacteristics.ContainsInitializedData)); // SizeOfUninitializedData: builder.WriteUInt32((uint)SumRawDataSizes(sections, SectionCharacteristics.ContainsUninitializedData)); // AddressOfEntryPoint: builder.WriteUInt32((uint)directories.AddressOfEntryPoint); // BaseOfCode: int codeSectionIndex = IndexOfSection(sections, SectionCharacteristics.ContainsCode); builder.WriteUInt32((uint)(codeSectionIndex != -1 ? sections[codeSectionIndex].RelativeVirtualAddress : 0)); if (Header.Is32Bit) { // BaseOfData: int dataSectionIndex = IndexOfSection(sections, SectionCharacteristics.ContainsInitializedData); builder.WriteUInt32((uint)(dataSectionIndex != -1 ? sections[dataSectionIndex].RelativeVirtualAddress : 0)); builder.WriteUInt32((uint)Header.ImageBase); } else { builder.WriteUInt64(Header.ImageBase); } // NT additional fields: builder.WriteUInt32((uint)Header.SectionAlignment); builder.WriteUInt32((uint)Header.FileAlignment); builder.WriteUInt16(Header.MajorOperatingSystemVersion); builder.WriteUInt16(Header.MinorOperatingSystemVersion); builder.WriteUInt16(Header.MajorImageVersion); builder.WriteUInt16(Header.MinorImageVersion); builder.WriteUInt16(Header.MajorSubsystemVersion); builder.WriteUInt16(Header.MinorSubsystemVersion); // Win32VersionValue (reserved, should be 0) builder.WriteUInt32(0); // SizeOfImage: var lastSection = sections[sections.Length - 1]; builder.WriteUInt32((uint)BitArithmetic.Align(lastSection.RelativeVirtualAddress + lastSection.VirtualSize, Header.SectionAlignment)); // SizeOfHeaders: builder.WriteUInt32((uint)BitArithmetic.Align(Header.ComputeSizeOfPeHeaders(sections.Length), Header.FileAlignment)); // Checksum: // Shall be zero for strong name signing. builder.WriteUInt32(0); builder.WriteUInt16((ushort)Header.Subsystem); builder.WriteUInt16((ushort)Header.DllCharacteristics); if (Header.Is32Bit) { builder.WriteUInt32((uint)Header.SizeOfStackReserve); builder.WriteUInt32((uint)Header.SizeOfStackCommit); builder.WriteUInt32((uint)Header.SizeOfHeapReserve); builder.WriteUInt32((uint)Header.SizeOfHeapCommit); } else { builder.WriteUInt64(Header.SizeOfStackReserve); builder.WriteUInt64(Header.SizeOfStackCommit); builder.WriteUInt64(Header.SizeOfHeapReserve); builder.WriteUInt64(Header.SizeOfHeapCommit); } // LoaderFlags builder.WriteUInt32(0); // The number of data-directory entries in the remainder of the header. builder.WriteUInt32(16); // directory entries: builder.WriteUInt32((uint)directories.ExportTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.ExportTable.Size); builder.WriteUInt32((uint)directories.ImportTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.ImportTable.Size); builder.WriteUInt32((uint)directories.ResourceTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.ResourceTable.Size); builder.WriteUInt32((uint)directories.ExceptionTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.ExceptionTable.Size); // Authenticode CertificateTable directory. Shall be zero before the PE is signed. builder.WriteUInt32(0); builder.WriteUInt32(0); builder.WriteUInt32((uint)directories.BaseRelocationTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.BaseRelocationTable.Size); builder.WriteUInt32((uint)directories.DebugTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.DebugTable.Size); builder.WriteUInt32((uint)directories.CopyrightTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.CopyrightTable.Size); builder.WriteUInt32((uint)directories.GlobalPointerTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.GlobalPointerTable.Size); builder.WriteUInt32((uint)directories.ThreadLocalStorageTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.ThreadLocalStorageTable.Size); builder.WriteUInt32((uint)directories.LoadConfigTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.LoadConfigTable.Size); builder.WriteUInt32((uint)directories.BoundImportTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.BoundImportTable.Size); builder.WriteUInt32((uint)directories.ImportAddressTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.ImportAddressTable.Size); builder.WriteUInt32((uint)directories.DelayImportTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.DelayImportTable.Size); builder.WriteUInt32((uint)directories.CorHeaderTable.RelativeVirtualAddress); builder.WriteUInt32((uint)directories.CorHeaderTable.Size); // Reserved, should be 0 builder.WriteUInt64(0); }
public void WriteAlignPad() { var writer = new BlobBuilder(4); writer.WriteByte(0x01); writer.PadTo(2); writer.WriteByte(0x02); writer.Align(4); writer.Align(4); writer.WriteByte(0x03); writer.Align(4); writer.WriteByte(0x04); writer.WriteByte(0x05); writer.Align(8); writer.WriteByte(0x06); writer.Align(2); writer.Align(1); AssertEx.Equal(new byte[] { 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00 }, writer.ToArray()); }
public void AddFinallyFaultFilterRegions() { var code = new BlobBuilder(); var flow = new ControlFlowBuilder(); var il = new InstructionEncoder(code, flow); var l1 = il.DefineLabel(); var l2 = il.DefineLabel(); var l3 = il.DefineLabel(); var l4 = il.DefineLabel(); var l5 = il.DefineLabel(); il.MarkLabel(l1); Assert.Equal(0, il.Offset); il.OpCode(ILOpCode.Nop); il.MarkLabel(l2); Assert.Equal(1, il.Offset); il.OpCode(ILOpCode.Nop); il.OpCode(ILOpCode.Nop); il.MarkLabel(l3); Assert.Equal(3, il.Offset); il.OpCode(ILOpCode.Nop); il.OpCode(ILOpCode.Nop); il.OpCode(ILOpCode.Nop); il.MarkLabel(l4); Assert.Equal(6, il.Offset); il.OpCode(ILOpCode.Nop); il.OpCode(ILOpCode.Nop); il.OpCode(ILOpCode.Nop); il.OpCode(ILOpCode.Nop); il.MarkLabel(l5); Assert.Equal(10, il.Offset); flow.AddFaultRegion(l1, l2, l3, l4); flow.AddFinallyRegion(l1, l2, l3, l4); flow.AddFilterRegion(l1, l2, l3, l4, l5); var builder = new BlobBuilder(); builder.WriteByte(0xff); flow.SerializeExceptionTable(builder); AssertEx.Equal(new byte[] { 0xFF, 0x00, 0x00, 0x00, // padding 0x01, // flag (byte)(builder.Count - 4), // size 0x00, 0x00, // reserved 0x04, 0x00, // kind 0x00, 0x00, // try offset 0x01, // try length 0x03, 0x00, // handler offset 0x03, // handler length 0x00, 0x00, 0x00, 0x00, // catch type or filter offset 0x02, 0x00, // kind 0x00, 0x00, // try offset 0x01, // try length 0x03, 0x00, // handler offset 0x03, // handler length 0x00, 0x00, 0x00, 0x00, // catch type or filter offset 0x01, 0x00, // kind 0x00, 0x00, // try offset 0x01, // try length 0x03, 0x00, // handler offset 0x03, // handler length 0x0A, 0x00, 0x00, 0x00 // catch type or filter offset }, builder.ToArray()); }
internal static ExceptionRegionEncoder SerializeTableHeader(BlobBuilder builder, int exceptionRegionCount, bool hasSmallRegions) { Debug.Assert(exceptionRegionCount > 0); const byte EHTableFlag = 0x01; const byte FatFormatFlag = 0x40; bool hasSmallFormat = hasSmallRegions && IsSmallRegionCount(exceptionRegionCount); int dataSize = GetExceptionTableSize(exceptionRegionCount, hasSmallFormat); builder.Align(4); if (hasSmallFormat) { builder.WriteByte(EHTableFlag); builder.WriteByte(unchecked((byte)dataSize)); builder.WriteInt16(0); } else { Debug.Assert(dataSize <= 0x00ffffff); builder.WriteByte(EHTableFlag | FatFormatFlag); builder.WriteByte(unchecked((byte)dataSize)); builder.WriteUInt16(unchecked((ushort)(dataSize >> 8))); } return new ExceptionRegionEncoder(builder, hasSmallFormat); }
private void EncodeType(BlobBuilder blobBuilder, TypeDesc type, EmbeddedSignatureDataEmitter signatureDataEmitter) { signatureDataEmitter.Push(); signatureDataEmitter.Push(); signatureDataEmitter.EmitAtCurrentIndexStack(blobBuilder); signatureDataEmitter.Pop(); signatureDataEmitter.Push(); if (type.IsPrimitive) { SignatureTypeCode primitiveCode; switch (type.Category) { case TypeFlags.Void: primitiveCode = SignatureTypeCode.Void; break; case TypeFlags.Boolean: primitiveCode = SignatureTypeCode.Boolean; break; case TypeFlags.Char: primitiveCode = SignatureTypeCode.Char; break; case TypeFlags.SByte: primitiveCode = SignatureTypeCode.SByte; break; case TypeFlags.Byte: primitiveCode = SignatureTypeCode.Byte; break; case TypeFlags.Int16: primitiveCode = SignatureTypeCode.Int16; break; case TypeFlags.UInt16: primitiveCode = SignatureTypeCode.UInt16; break; case TypeFlags.Int32: primitiveCode = SignatureTypeCode.Int32; break; case TypeFlags.UInt32: primitiveCode = SignatureTypeCode.UInt32; break; case TypeFlags.Int64: primitiveCode = SignatureTypeCode.Int64; break; case TypeFlags.UInt64: primitiveCode = SignatureTypeCode.UInt64; break; case TypeFlags.IntPtr: primitiveCode = SignatureTypeCode.IntPtr; break; case TypeFlags.UIntPtr: primitiveCode = SignatureTypeCode.UIntPtr; break; case TypeFlags.Single: primitiveCode = SignatureTypeCode.Single; break; case TypeFlags.Double: primitiveCode = SignatureTypeCode.Double; break; default: throw new Exception("Unknown primitive type"); } blobBuilder.WriteByte((byte)primitiveCode); } else if (type.IsSzArray) { blobBuilder.WriteByte((byte)SignatureTypeCode.SZArray); EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter); } else if (type.IsArray) { var arrayType = (ArrayType)type; blobBuilder.WriteByte((byte)SignatureTypeCode.Array); EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter); signatureDataEmitter.EmitArrayShapeAtCurrentIndexStack(blobBuilder, arrayType.Rank); } else if (type.IsPointer) { blobBuilder.WriteByte((byte)SignatureTypeCode.Pointer); EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter); } else if (type.IsFunctionPointer) { FunctionPointerType fnptrType = (FunctionPointerType)type; blobBuilder.WriteByte((byte)SignatureTypeCode.FunctionPointer); EncodeMethodSignature(blobBuilder, fnptrType.Signature, signatureDataEmitter); } else if (type.IsByRef) { blobBuilder.WriteByte((byte)SignatureTypeCode.ByReference); EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter); } else if (type.IsObject) { blobBuilder.WriteByte((byte)SignatureTypeCode.Object); } else if (type.IsString) { blobBuilder.WriteByte((byte)SignatureTypeCode.String); } else if (type.IsWellKnownType(WellKnownType.TypedReference)) { blobBuilder.WriteByte((byte)SignatureTypeCode.TypedReference); } else if (type.IsWellKnownType(WellKnownType.Void)) { blobBuilder.WriteByte((byte)SignatureTypeCode.Void); } else if (type is SignatureVariable) { SignatureVariable sigVar = (SignatureVariable)type; SignatureTypeCode code = sigVar.IsMethodSignatureVariable ? SignatureTypeCode.GenericMethodParameter : SignatureTypeCode.GenericTypeParameter; blobBuilder.WriteByte((byte)code); blobBuilder.WriteCompressedInteger(sigVar.Index); } else if (type is InstantiatedType) { blobBuilder.WriteByte((byte)SignatureTypeCode.GenericTypeInstance); EncodeType(blobBuilder, type.GetTypeDefinition(), signatureDataEmitter); blobBuilder.WriteCompressedInteger(type.Instantiation.Length); foreach (var instantiationArg in type.Instantiation) { EncodeType(blobBuilder, instantiationArg, signatureDataEmitter); } } else if (type is MetadataType) { var metadataType = (MetadataType)type; // Must be class or valuetype blobBuilder.WriteByte(type.IsValueType ? (byte)SignatureTypeKind.ValueType : (byte)SignatureTypeKind.Class); int codedIndex = CodedIndex.TypeDefOrRef(GetTypeRef(metadataType)); blobBuilder.WriteCompressedInteger(codedIndex); } else { throw new Exception("Unexpected type"); } signatureDataEmitter.Pop(); signatureDataEmitter.Pop(); }
private void SerializeHeader(BlobBuilder writer, Sizes sizes) { // signature: writer.WriteByte((byte)'D'); writer.WriteByte((byte)'A'); writer.WriteByte((byte)'M'); writer.WriteByte((byte)'D'); // version: 0.2 writer.WriteByte(0); writer.WriteByte(2); // table sizes: writer.WriteInt32(_documentTable.Count); writer.WriteInt32(_methodTable.Count); // blob heap sizes: writer.WriteInt32(sizes.GuidHeapSize); writer.WriteInt32(sizes.BlobHeapSize); }
internal void FixupBranches(BlobBuilder srcBuilder, BlobBuilder dstBuilder) { int srcOffset = 0; var branch = _branches[0]; int branchIndex = 0; int blobOffset = 0; foreach (Blob blob in srcBuilder.GetBlobs()) { Debug.Assert(blobOffset == 0 || blobOffset == 1 && blob.Buffer[blobOffset - 1] == 0xff); while (true) { // copy bytes preceding the next branch, or till the end of the blob: int chunkSize = Math.Min(branch.ILOffset - srcOffset, blob.Length - blobOffset); dstBuilder.WriteBytes(blob.Buffer, blobOffset, chunkSize); srcOffset += chunkSize; blobOffset += chunkSize; // there is no branch left in the blob: if (blobOffset == blob.Length) { blobOffset = 0; break; } Debug.Assert(blob.Buffer[blobOffset] == branch.ShortOpCode && (blobOffset + 1 == blob.Length || blob.Buffer[blobOffset + 1] == 0xff)); srcOffset += sizeof(byte) + sizeof(sbyte); // write actual branch instruction: int branchDistance; if (branch.IsShortBranchDistance(_labels, out branchDistance)) { dstBuilder.WriteByte(branch.ShortOpCode); dstBuilder.WriteSByte((sbyte)branchDistance); } else { dstBuilder.WriteByte((byte)((ILOpCode)branch.ShortOpCode).GetLongBranch()); dstBuilder.WriteInt32(branchDistance); } // next branch: branchIndex++; if (branchIndex == _branches.Count) { branch = new BranchInfo(int.MaxValue, default(LabelHandle), 0); } else { branch = _branches[branchIndex]; } // the branch starts at the very end and its operand is in the next blob: if (blobOffset == blob.Length - 1) { blobOffset = 1; break; } // skip fake branch instruction: blobOffset += sizeof(byte) + sizeof(sbyte); } } }
private static void WriteNameTable(BlobBuilder builder) { int start = builder.Count; foreach (char ch in CorEntryPointDll) { builder.WriteByte((byte)ch); } builder.WriteByte(0); builder.WriteUInt16(0); Debug.Assert(builder.Count - start == SizeOfNameTable); }
private void WriteRuntimeStartupStub(BlobBuilder sectionBuilder, int importAddressTableRva, ulong baseAddress) { // entry point code, consisting of a jump indirect to _CorXXXMain if (Is32Bit) { // Write zeros (nops) to pad the entry point code so that the target address is aligned on a 4 byte boundary. // Note that the section is aligned to FileAlignment, which is at least 512, so we can align relatively to the start of the section. sectionBuilder.Align(4); sectionBuilder.WriteUInt16(0); sectionBuilder.WriteByte(0xff); sectionBuilder.WriteByte(0x25); //4 sectionBuilder.WriteUInt32((uint)importAddressTableRva + (uint)baseAddress); //8 } else { // Write zeros (nops) to pad the entry point code so that the target address is aligned on a 8 byte boundary. // Note that the section is aligned to FileAlignment, which is at least 512, so we can align relatively to the start of the section. sectionBuilder.Align(8); sectionBuilder.WriteUInt32(0); sectionBuilder.WriteUInt16(0); sectionBuilder.WriteByte(0xff); sectionBuilder.WriteByte(0x25); //8 sectionBuilder.WriteUInt64((ulong)importAddressTableRva + baseAddress); //16 } }
public void WriteContentToBlobBuilder() { var builder1 = new BlobBuilder(16); for (int i = 0; i < 20; i++) { builder1.WriteByte((byte)i); } var builder2 = new BlobBuilder(256); builder1.WriteContentTo(builder2); AssertEx.Equal(new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13 }, builder2.ToArray()); builder1.WriteByte(0xff); builder1.WriteContentTo(builder2); AssertEx.Equal(new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xff, }, builder2.ToArray()); }
private void SerializeMetadataHeader(BlobBuilder writer) { int startOffset = writer.Position; // signature writer.WriteUInt32(0x424A5342); // major version writer.WriteUInt16(1); // minor version writer.WriteUInt16(1); // reserved writer.WriteUInt32(0); // metadata version length writer.WriteUInt32(MetadataSizes.MetadataVersionPaddedLength); int n = Math.Min(MetadataSizes.MetadataVersionPaddedLength, _metadataVersion.Length); for (int i = 0; i < n; i++) { writer.WriteByte((byte)_metadataVersion[i]); } for (int i = n; i < MetadataSizes.MetadataVersionPaddedLength; i++) { writer.WriteByte(0); } // reserved writer.WriteUInt16(0); // number of streams writer.WriteUInt16((ushort)(5 + (_sizes.IsMinimalDelta ? 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", writer); } // 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.IsMetadataTableStreamCompressed ? "#~" : "#-"), writer); SerializeStreamHeader(ref offsetFromStartOfMetadata, _sizes.GetAlignedHeapSize(HeapIndex.String), "#Strings", writer); SerializeStreamHeader(ref offsetFromStartOfMetadata, _sizes.GetAlignedHeapSize(HeapIndex.UserString), "#US", writer); SerializeStreamHeader(ref offsetFromStartOfMetadata, _sizes.GetAlignedHeapSize(HeapIndex.Guid), "#GUID", writer); SerializeStreamHeader(ref offsetFromStartOfMetadata, _sizes.GetAlignedHeapSize(HeapIndex.Blob), "#Blob", writer); if (_sizes.IsMinimalDelta) { SerializeStreamHeader(ref offsetFromStartOfMetadata, 0, "#JTD", writer); } int endOffset = writer.Position; Debug.Assert(endOffset - startOffset == _sizes.MetadataHeaderSize); }
private static void WriteSectionHeader(SectionHeader sectionHeader, BlobBuilder writer) { if (sectionHeader.VirtualSize == 0) { return; } for (int j = 0, m = sectionHeader.Name.Length; j < 8; j++) { if (j < m) { writer.WriteByte((byte)sectionHeader.Name[j]); } else { writer.WriteByte(0); } } writer.WriteUInt32((uint)sectionHeader.VirtualSize); writer.WriteUInt32((uint)sectionHeader.RelativeVirtualAddress); writer.WriteUInt32((uint)sectionHeader.SizeOfRawData); writer.WriteUInt32((uint)sectionHeader.PointerToRawData); writer.WriteUInt32((uint)sectionHeader.PointerToRelocations); writer.WriteUInt32((uint)sectionHeader.PointerToLinenumbers); writer.WriteUInt16(sectionHeader.NumberOfRelocations); writer.WriteUInt16(sectionHeader.NumberOfLinenumbers); writer.WriteUInt32((uint)sectionHeader.Characteristics); }
private static void WriteSectionHeader(BlobBuilder builder, Section section, SerializedSection serializedSection) { if (serializedSection.VirtualSize == 0) { return; } for (int j = 0, m = section.Name.Length; j < 8; j++) { if (j < m) { builder.WriteByte((byte)section.Name[j]); } else { builder.WriteByte(0); } } builder.WriteUInt32((uint)serializedSection.VirtualSize); builder.WriteUInt32((uint)serializedSection.RelativeVirtualAddress); builder.WriteUInt32((uint)serializedSection.SizeOfRawData); builder.WriteUInt32((uint)serializedSection.PointerToRawData); // PointerToRelocations (TODO: not supported): builder.WriteUInt32(0); // PointerToLinenumbers (TODO: not supported): builder.WriteUInt32(0); // NumberOfRelocations (TODO: not supported): builder.WriteUInt16(0); // NumberOfLinenumbers (TODO: not supported): builder.WriteUInt16(0); builder.WriteUInt32((uint)section.Characteristics); }
private static void WriteNameTable(Stream peStream) { var writer = new BlobBuilder(SizeOfNameTable); foreach (char ch in CorEntryPointDll) { writer.WriteByte((byte)ch); } writer.WriteByte(0); writer.WriteUInt16(0); Debug.Assert(writer.Count == SizeOfNameTable); writer.WriteContentTo(peStream); }
public void LinkSuffix1() { var builder1 = new BlobBuilder(16); builder1.WriteByte(1); builder1.WriteByte(2); builder1.WriteByte(3); var builder2 = new BlobBuilder(16); builder2.WriteByte(4); builder1.LinkSuffix(builder2); AssertEx.Equal(new byte[] { 1, 2, 3, 4 }, builder1.ToArray()); Assert.Equal(4, builder1.Count); Assert.Equal(1, builder2.Count); var builder3 = new BlobBuilder(16); builder3.WriteByte(5); var builder4 = new BlobBuilder(16); builder4.WriteByte(6); builder3.LinkSuffix(builder4); builder1.LinkSuffix(builder3); AssertEx.Equal(new byte[] { 1, 2, 3, 4, 5, 6 }, builder1.ToArray()); Assert.Equal(6, builder1.Count); Assert.Equal(2, builder3.Count); Assert.Equal(1, builder4.Count); }
/// <exception cref="InvalidOperationException" /> internal void CopyCodeAndFixupBranches(BlobBuilder srcBuilder, BlobBuilder dstBuilder) { var branch = _branches[0]; int branchIndex = 0; // offset within the source builder int srcOffset = 0; // current offset within the current source blob int srcBlobOffset = 0; foreach (Blob srcBlob in srcBuilder.GetBlobs()) { Debug.Assert( srcBlobOffset == 0 || srcBlobOffset == 1 && srcBlob.Buffer[0] == 0xff || srcBlobOffset == 4 && srcBlob.Buffer[0] == 0xff && srcBlob.Buffer[1] == 0xff && srcBlob.Buffer[2] == 0xff && srcBlob.Buffer[3] == 0xff); while (true) { // copy bytes preceding the next branch, or till the end of the blob: int chunkSize = Math.Min(branch.ILOffset - srcOffset, srcBlob.Length - srcBlobOffset); dstBuilder.WriteBytes(srcBlob.Buffer, srcBlobOffset, chunkSize); srcOffset += chunkSize; srcBlobOffset += chunkSize; // there is no branch left in the blob: if (srcBlobOffset == srcBlob.Length) { srcBlobOffset = 0; break; } Debug.Assert(srcBlob.Buffer[srcBlobOffset] == (byte)branch.OpCode); int operandSize = branch.OpCode.GetBranchOperandSize(); bool isShortInstruction = operandSize == 1; // Note: the 4B operand is contiguous since we wrote it via BlobBuilder.WriteInt32() Debug.Assert( srcBlobOffset + 1 == srcBlob.Length || (isShortInstruction ? srcBlob.Buffer[srcBlobOffset + 1] == 0xff : BitConverter.ToUInt32(srcBlob.Buffer, srcBlobOffset + 1) == 0xffffffff)); // write branch opcode: dstBuilder.WriteByte(srcBlob.Buffer[srcBlobOffset]); // write branch operand: int branchDistance; bool isShortDistance = branch.IsShortBranchDistance(_labels, out branchDistance); if (isShortInstruction && !isShortDistance) { // We could potentially implement algortihm that automatically fixes up the branch instructions as well to accomodate bigger distances, // however an optimal algorithm would be rather complex (something like: calculate topological ordering of crossing branch instructions // and then use fixed point to eliminate cycles). If the caller doesn't care about optimal IL size they can use long branches whenever the // distance is unknown upfront. If they do they probably already implement more sophisticad algorithm for IL layout optimization already. throw new InvalidOperationException(SR.Format(SR.DistanceBetweenInstructionAndLabelTooBig, branch.OpCode, srcOffset, branchDistance)); } if (isShortInstruction) { dstBuilder.WriteSByte((sbyte)branchDistance); } else { dstBuilder.WriteInt32(branchDistance); } srcOffset += sizeof(byte) + operandSize; // next branch: branchIndex++; if (branchIndex == _branches.Count) { branch = new BranchInfo(int.MaxValue, default(LabelHandle), 0); } else { branch = _branches[branchIndex]; } // the branch starts at the very end and its operand is in the next blob: if (srcBlobOffset == srcBlob.Length - 1) { srcBlobOffset = operandSize; break; } // skip fake branch instruction: srcBlobOffset += sizeof(byte) + operandSize; } } }
private void WriteImportTable(Stream peStream, int importTableRva, int importAddressTableRva) { var writer = new BlobBuilder(SizeOfImportTable); int ilRVA = importTableRva + 40; int hintRva = ilRVA + (_is32bit ? 12 : 16); int nameRva = hintRva + 12 + 2; // Import table writer.WriteUInt32((uint)ilRVA); // 4 writer.WriteUInt32(0); // 8 writer.WriteUInt32(0); // 12 writer.WriteUInt32((uint)nameRva); // 16 writer.WriteUInt32((uint)importAddressTableRva); // 20 writer.WriteBytes(0, 20); // 40 // Import Lookup table if (_is32bit) { writer.WriteUInt32((uint)hintRva); // 44 writer.WriteUInt32(0); // 48 writer.WriteUInt32(0); // 52 } else { writer.WriteUInt64((uint)hintRva); // 48 writer.WriteUInt64(0); // 56 } // Hint table writer.WriteUInt16(0); // Hint 54|58 foreach (char ch in CorEntryPointName) { writer.WriteByte((byte)ch); // 65|69 } writer.WriteByte(0); // 66|70 Debug.Assert(writer.Count == SizeOfImportTable); writer.WriteContentTo(peStream); }
public void LinkSuffix_Empty1() { var builder1 = new BlobBuilder(16); var builder2 = new BlobBuilder(16); builder2.WriteByte(2); builder1.LinkSuffix(builder2); AssertEx.Equal(new byte[] { 0x02 }, builder1.ToArray()); Assert.Equal(1, builder1.Count); Assert.Equal(1, builder2.Count); }
private void WriteRuntimeStartupStub(Stream peStream, int importAddressTableRva) { var writer = new BlobBuilder(16); // entry point code, consisting of a jump indirect to _CorXXXMain if (_is32bit) { //emit 0's (nops) to pad the entry point code so that the target address is aligned on a 4 byte boundary. for (uint i = 0, n = (uint)(BitArithmeticUtilities.Align((uint)peStream.Position, 4) - peStream.Position); i < n; i++) { writer.WriteByte(0); } writer.WriteUInt16(0); writer.WriteByte(0xff); writer.WriteByte(0x25); //4 writer.WriteUInt32((uint)importAddressTableRva + (uint)_properties.BaseAddress); //8 } else { //emit 0's (nops) to pad the entry point code so that the target address is aligned on a 8 byte boundary. for (uint i = 0, n = (uint)(BitArithmeticUtilities.Align((uint)peStream.Position, 8) - peStream.Position); i < n; i++) { writer.WriteByte(0); } writer.WriteUInt32(0); writer.WriteUInt16(0); writer.WriteByte(0xff); writer.WriteByte(0x25); //8 writer.WriteUInt64((ulong)importAddressTableRva + _properties.BaseAddress); //16 } writer.WriteContentTo(peStream); }
public void LinkPrefix1() { var builder1 = new BlobBuilder(16); builder1.WriteByte(1); builder1.WriteByte(2); builder1.WriteByte(3); var builder2 = new BlobBuilder(16); builder2.WriteByte(4); builder1.LinkPrefix(builder2); AssertEx.Equal(new byte[] { 4, 1, 2, 3 }, builder1.ToArray()); Assert.Equal(4, builder1.Count); Assert.Equal(1, builder2.Count); }
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()); }