public void GetBlobs() { var builder = new BlobBuilder(16); builder.WriteBytes(1, 100); var blobs = builder.GetBlobs().ToArray(); Assert.Equal(2, blobs.Length); Assert.Equal(16, blobs[0].Length); Assert.Equal(100 - 16, blobs[1].Length); builder.WriteByte(1); blobs = builder.GetBlobs().ToArray(); Assert.Equal(3, blobs.Length); Assert.Equal(16, blobs[0].Length); Assert.Equal(16, blobs[0].GetBytes().Array.Length); Assert.Equal(100 - 16, blobs[1].Length); Assert.Equal(100 - 16, blobs[1].GetBytes().Array.Length); Assert.Equal(1, blobs[2].Length); Assert.Equal(100 - 16, blobs[2].GetBytes().Array.Length); builder.Clear(); blobs = builder.GetBlobs().ToArray(); Assert.Equal(1, blobs.Length); Assert.Equal(0, blobs[0].Length); // Clear uses the first buffer: Assert.Equal(16, blobs[0].GetBytes().Array.Length); }
private static int WriteEmbeddedPortablePdbData(BlobBuilder builder, BlobBuilder debugMetadata) { int start = builder.Count; // header (signature, decompressed size): builder.WriteUInt32(PortablePdbVersions.DebugDirectoryEmbeddedSignature); builder.WriteInt32(debugMetadata.Count); // compressed data: var compressed = new MemoryStream(); using (var deflate = new DeflateStream(compressed, CompressionLevel.Optimal, leaveOpen: true)) { foreach (var blob in debugMetadata.GetBlobs()) { var segment = blob.GetBytes(); deflate.Write(segment.Array, segment.Offset, segment.Count); } } // TODO: avoid multiple copies: builder.WriteBytes(compressed.ToArray()); return(builder.Count - start); }
public void GetContentToSign_MultiBlobHeader() { var builder = new BlobBuilder(16); builder.WriteBytes(0, 16); builder.WriteBytes(1, 16); builder.WriteBytes(2, 16); builder.WriteBytes(3, 16); builder.WriteBytes(4, 2); var snFixup = builder.ReserveBytes(1); builder.WriteBytes(4, 13); builder.WriteBytes(5, 10); Assert.Equal(6, builder.GetBlobs().Count()); AssertEx.Equal( new[] { "0: [0, 16)", "1: [0, 16)", "2: [0, 1)", "4: [0, 2)", "4: [3, 16)", "5: [0, 10)" }, GetBlobRanges(builder, PEBuilder.GetContentToSign(builder, peHeadersSize: 33, peHeaderAlignment: 64, strongNameSignatureFixup: snFixup))); }
public void GetContentToSign_HeaderAndFixupInDistinctBlobs() { var builder = new BlobBuilder(16); builder.WriteBytes(0, 16); builder.WriteBytes(1, 16); builder.WriteBytes(2, 16); builder.WriteBytes(3, 16); var snFixup = builder.ReserveBytes(16); builder.WriteBytes(5, 16); builder.WriteBytes(6, 1); Assert.Equal(7, builder.GetBlobs().Count()); AssertEx.Equal( new[] { "0: [0, 1)", "0: [4, 16)", "1: [0, 16)", "2: [0, 16)", "3: [0, 16)", "4: [0, 0)", "4: [16, 16)", "5: [0, 16)", "6: [0, 1)" }, GetBlobRanges(builder, PEBuilder.GetContentToSign(builder, peHeadersSize: 1, peHeaderAlignment: 4, strongNameSignatureFixup: snFixup))); }
/// <summary> /// Serializes Portable PDB content into the given <see cref="BlobBuilder"/>. /// </summary> /// <param name="builder">Builder to write to.</param> /// <returns>The id of the serialized content.</returns> /// <exception cref="ArgumentNullException"><paramref name="builder"/> is null.</exception> public BlobContentId Serialize(BlobBuilder builder) { if (builder == null) { Throw.ArgumentNull(nameof(builder)); } // header: MetadataBuilder.SerializeMetadataHeader(builder, MetadataVersion, _serializedMetadata.Sizes); // #Pdb stream SerializeStandalonePdbStream(builder); // #~ or #- stream: _builder.SerializeMetadataTables(builder, _serializedMetadata.Sizes, _serializedMetadata.StringMap, methodBodyStreamRva: 0, mappedFieldDataStreamRva: 0); // #Strings, #US, #Guid and #Blob streams: _builder.WriteHeapsTo(builder, _serializedMetadata.StringHeap); var contentId = IdProvider(builder.GetBlobs()); // fill in the id: var idWriter = new BlobWriter(_pdbIdBlob); idWriter.WriteGuid(contentId.Guid); idWriter.WriteUInt32(contentId.Stamp); Debug.Assert(idWriter.RemainingBytes == 0); return(contentId); }
public BlobContentId Serialize(BlobBuilder builder) { // Define and serialize sections in two steps. // We need to know about all sections before serializing them. var serializedSections = SerializeSections(); // The positions and sizes of directories are calculated during section serialization. var directories = GetDirectories(); Blob stampFixup; WritePESignature(builder); WriteCoffHeader(builder, serializedSections, out stampFixup); WritePEHeader(builder, directories, serializedSections); WriteSectionHeaders(builder, serializedSections); builder.Align(Header.FileAlignment); foreach (var section in serializedSections) { builder.LinkSuffix(section.Builder); builder.Align(Header.FileAlignment); } var contentId = IdProvider(builder.GetBlobs()); // patch timestamp in COFF header: var stampWriter = new BlobWriter(stampFixup); stampWriter.WriteUInt32(contentId.Stamp); Debug.Assert(stampWriter.RemainingBytes == 0); return(contentId); }
private IEnumerable <Blob> GetContentToSign(BlobBuilder peImage) { // Signed content includes // - PE header without its alignment padding // - all sections including their alignment padding and excluding strong name signature blob int remainingHeader = Header.ComputeSizeOfPeHeaders(Sections.Length); foreach (var blob in peImage.GetBlobs()) { if (remainingHeader > 0) { int length = Math.Min(remainingHeader, blob.Length); yield return(new Blob(blob.Buffer, blob.Start, length)); remainingHeader -= length; } else if (blob.Buffer == _lazyStrongNameSignature.Buffer) { yield return(new Blob(blob.Buffer, blob.Start, _lazyStrongNameSignature.Start - blob.Start)); yield return(new Blob(blob.Buffer, _lazyStrongNameSignature.Start + _lazyStrongNameSignature.Length, blob.Length - _lazyStrongNameSignature.Length)); } else { yield return(new Blob(blob.Buffer, blob.Start, blob.Length)); } } }
internal static void AppendData(this IncrementalHash hash, BlobBuilder builder) { foreach (var blob in builder.GetBlobs()) { hash.AppendData(blob.GetBytes()); } }
// internal for testing internal static IEnumerable <Blob> GetContentToSign(BlobBuilder peImage, int peHeadersSize, int peHeaderAlignment, Blob strongNameSignatureFixup) { // Signed content includes // - PE header without its alignment padding // - all sections including their alignment padding and excluding strong name signature blob // PE specification: // To calculate the PE image hash, Authenticode orders the sections that are specified in the section table // by address range, then hashes the resulting sequence of bytes, passing over the exclusion ranges. // // Note that sections are by construction ordered by their address, so there is no need to reorder. int remainingHeaderToSign = peHeadersSize; int remainingHeader = BitArithmetic.Align(peHeadersSize, peHeaderAlignment); foreach (var blob in peImage.GetBlobs()) { int blobStart = blob.Start; int blobLength = blob.Length; while (blobLength > 0) { if (remainingHeader > 0) { int length; if (remainingHeaderToSign > 0) { length = Math.Min(remainingHeaderToSign, blobLength); yield return(new Blob(blob.Buffer, blobStart, length)); remainingHeaderToSign -= length; } else { length = Math.Min(remainingHeader, blobLength); } remainingHeader -= length; blobStart += length; blobLength -= length; } else if (blob.Buffer == strongNameSignatureFixup.Buffer) { yield return(GetPrefixBlob(new Blob(blob.Buffer, blobStart, blobLength), strongNameSignatureFixup)); yield return(GetSuffixBlob(new Blob(blob.Buffer, blobStart, blobLength), strongNameSignatureFixup)); break; } else { yield return(new Blob(blob.Buffer, blobStart, blobLength)); break; } } } }
private static byte[] ComputeSigningHash( BlobBuilder peImage, PEHeaders peHeaders, Blob checksumBlob, int strongNameOffset, int strongNameSize) { const int SectionHeaderSize = 40; bool is32bit = peHeaders.PEHeader.Magic == PEMagic.PE32; int peHeadersSize = peHeaders.PEHeaderStartOffset + PEHeaderSize(is32bit) + SectionHeaderSize * peHeaders.SectionHeaders.Length; // Signature is calculated with the checksum and authenticode signature zeroed new BlobWriter(checksumBlob).WriteUInt32(0); var buffer = peImage.GetBlobs().Single().GetBytes().Array; int authenticodeOffset = GetAuthenticodeOffset(peHeaders, is32bit); var authenticodeDir = peHeaders.PEHeader.CertificateTableDirectory; for (int i = 0; i < 2 * sizeof(int); i++) { buffer[authenticodeOffset + i] = 0; } using (var hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA1)) { // First hash the DOS header and PE headers hash.AppendData(buffer, 0, peHeadersSize); // Now each section, skipping the strong name signature if present foreach (var sectionHeader in peHeaders.SectionHeaders) { int sectionOffset = sectionHeader.PointerToRawData; int sectionSize = sectionHeader.SizeOfRawData; if ((strongNameOffset + strongNameSize) < sectionOffset || strongNameOffset >= (sectionOffset + sectionSize)) { // No signature overlap, hash the whole section hash.AppendData(buffer, sectionOffset, sectionSize); } else { // There is overlap. Hash both sides of signature hash.AppendData(buffer, sectionOffset, strongNameOffset - sectionOffset); var strongNameEndOffset = strongNameOffset + strongNameSize; hash.AppendData(buffer, strongNameEndOffset, sectionSize - (strongNameEndOffset - sectionOffset)); } } return(hash.GetHashAndReset()); } }
/// <summary> /// Relocation helper stores the output stream and initializes the PE blob builder enumerator. /// </summary> /// <param name="outputStream">Output stream for the relocated PE file</param> /// <param name="peFileBuilder">PE file blob builder</param> public RelocationHelper(Stream outputStream, ulong defaultImageBase, BlobBuilder peFileBuilder) { _outputStream = outputStream; _outputFilePos = 0; _defaultImageBase = defaultImageBase; _peFileLength = peFileBuilder.Count; _peFileBlobs = peFileBuilder.GetBlobs(); FetchNextBlob(); }
public void SerializeMetadata(BlobBuilder builder, out BlobContentId contentId) { SerializeMetadataImpl(builder, methodBodyStreamRva: 0, mappedFieldDataStreamRva: 0); contentId = IdProvider(builder.GetBlobs()); // fill in the id: var idWriter = new BlobWriter(_pdbIdBlob); idWriter.WriteGuid(contentId.Guid); idWriter.WriteUInt32(contentId.Stamp); Debug.Assert(idWriter.RemainingBytes == 0); }
private static IEnumerable <string> GetBlobRanges(BlobBuilder builder, IEnumerable <Blob> blobs) { var blobIndex = new Dictionary <byte[], int>(); int i = 0; foreach (var blob in builder.GetBlobs()) { blobIndex.Add(blob.Buffer, i++); } foreach (var blob in blobs) { yield return($"{blobIndex[blob.Buffer]}: [{blob.Start}, {blob.Start + blob.Length})"); } }
// internal for testing internal static IEnumerable <Blob> GetContentToChecksum(BlobBuilder peImage, Blob checksumFixup) { foreach (var blob in peImage.GetBlobs()) { if (blob.Buffer == checksumFixup.Buffer) { yield return(GetPrefixBlob(blob, checksumFixup)); yield return(GetSuffixBlob(blob, checksumFixup)); } else { yield return(blob); } } }
public void ReserveBytes2() { var builder = new BlobBuilder(16); var writer = new BlobWriter(builder.ReserveBytes(17)); writer.WriteBytes(1, 17); var blobs = builder.GetBlobs().ToArray(); Assert.Equal(1, blobs.Length); AssertEx.Equal(new byte[] { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, blobs[0].GetBytes().ToArray()); }
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 void GetContentToSign_AllInOneBlob() { var builder = new BlobBuilder(16); builder.WriteBytes(1, 5); var snFixup = builder.ReserveBytes(5); builder.WriteBytes(2, 6); Assert.Equal(1, builder.GetBlobs().Count()); AssertEx.Equal( new[] { "0: [0, 2)", "0: [4, 5)", "0: [10, 16)" }, GetBlobRanges(builder, PEBuilder.GetContentToSign(builder, peHeadersSize: 2, peHeaderAlignment: 4, strongNameSignatureFixup: snFixup))); }
private static bool TestChecksumAndAuthenticodeSignature(Stream peStream, byte[] privateKeyOpt = null) { var peHeaders = new PEHeaders(peStream); bool is32bit = peHeaders.PEHeader.Magic == PEMagic.PE32; uint expectedChecksum = peHeaders.PEHeader.CheckSum; int peHeadersSize = peHeaders.PEHeaderStartOffset + PEHeader.Size(is32bit) + SectionHeader.Size * peHeaders.SectionHeaders.Length; peStream.Position = 0; if (expectedChecksum == 0) { // not signed return(false); } int peSize = (int)peStream.Length; var peImage = new BlobBuilder(peSize); Assert.Equal(peSize, peImage.TryWriteBytes(peStream, peSize)); var buffer = peImage.GetBlobs().Single().Buffer; var checksumBlob = new Blob(buffer, peHeaders.PEHeaderStartOffset + PEHeader.OffsetOfChecksum, sizeof(uint)); uint checksum = PEBuilder.CalculateChecksum(peImage, checksumBlob); Assert.Equal(expectedChecksum, checksum); // validate signature: if (privateKeyOpt != null) { // signature is calculated with checksum zeroed: new BlobWriter(checksumBlob).WriteUInt32(0); int snOffset; Assert.True(peHeaders.TryGetDirectoryOffset(peHeaders.CorHeader.StrongNameSignatureDirectory, out snOffset)); var snBlob = new Blob(buffer, snOffset, peHeaders.CorHeader.StrongNameSignatureDirectory.Size); var expectedSignature = snBlob.GetBytes().ToArray(); var signature = SigningUtilities.CalculateRsaSignature(PEBuilder.GetContentToSign(peImage, peHeadersSize, peHeaders.PEHeader.FileAlignment, snBlob), privateKeyOpt); AssertEx.Equal(expectedSignature, signature); } return(true); }
/// <summary> /// Create a byte array representation of the complete fixup blob. /// </summary> public byte[] ToArray() { int totalBytes = (_nibbleCount + 1) >> 1; byte[] output = new byte[totalBytes]; int smallBytes = Math.Min(totalBytes, NibbleCountInSmallBuffer); for (int byteIndex = 0; byteIndex < smallBytes; byteIndex++) { output[byteIndex] = unchecked ((byte)(_smallBuffer >> (8 * byteIndex))); } if (_nibbleCount > NibbleCountInSmallBuffer) { int startOffset = ByteCountInSmallBuffer; if (_largeBuffer != null) { foreach (Blob blob in _largeBuffer.GetBlobs()) { ArraySegment <byte> blobSegment = blob.GetBytes(); Array.Copy(blobSegment.Array, blobSegment.Offset, output, startOffset, blob.Length); startOffset += blob.Length; } } if ((_nibbleCount & 1) != 0) { // Emit the last pending half-nibble output[startOffset] = _pendingNibble; startOffset++; } Debug.Assert(startOffset == totalBytes); } return(output); }
/// <summary> /// Validates that the given stream is marked as signed, the signature matches /// the public key, and the header checksum is correct. /// </summary> public static bool IsStreamFullSigned(Stream moduleContents) { var savedPosition = moduleContents.Position; try { moduleContents.Position = 0; var peHeaders = new PEHeaders(moduleContents); moduleContents.Position = 0; using (var metadata = ModuleMetadata.CreateFromStream(moduleContents, leaveOpen: true)) { var metadataReader = metadata.MetadataReader; var peReader = metadata.Module.PEReaderOpt; var flags = peHeaders.CorHeader.Flags; if (CorFlags.StrongNameSigned != (flags & CorFlags.StrongNameSigned)) { return(false); } var snDirectory = peReader.PEHeaders.CorHeader.StrongNameSignatureDirectory; if (!peHeaders.TryGetDirectoryOffset(snDirectory, out int snOffset)) { return(false); } moduleContents.Position = 0; int peSize; try { peSize = checked ((int)moduleContents.Length); } catch { return(false); } var peImage = new BlobBuilder(peSize); if (peSize != peImage.TryWriteBytes(moduleContents, peSize)) { return(false); } byte[] buffer = GetBlobBuffer(peImage.GetBlobs().Single()); uint expectedChecksum = peHeaders.PEHeader.CheckSum; Blob checksumBlob = MakeBlob(buffer, peHeaders.PEHeaderStartOffset + ChecksumOffset, sizeof(uint)); if (expectedChecksum != PeWriter.CalculateChecksum(peImage, checksumBlob)) { return(false); } int snSize = snDirectory.Size; byte[] hash = ComputeSigningHash(peImage, peHeaders, checksumBlob, snOffset, snSize); ImmutableArray <byte> publicKeyBlob = metadataReader.GetBlobContent(metadataReader.GetAssemblyDefinition().PublicKey); // RSA parameters start after the public key offset byte[] publicKeyParams = new byte[publicKeyBlob.Length - CryptoBlobParser.s_publicKeyHeaderSize]; publicKeyBlob.CopyTo(CryptoBlobParser.s_publicKeyHeaderSize, publicKeyParams, 0, publicKeyParams.Length); var snKey = publicKeyParams.ToRSAParameters(includePrivateParameters: false); using (var rsa = RSA.Create()) { rsa.ImportParameters(snKey); var reversedSignature = peReader.GetSectionData(snDirectory.RelativeVirtualAddress).GetContent(0, snSize).ToArray(); // Unknown why the signature is reversed, but this matches the behavior of the CLR // signing implementation. Array.Reverse(reversedSignature); if (!rsa.VerifyHash(hash, reversedSignature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1)) { return(false); } } return(true); } } finally { moduleContents.Position = savedPosition; } }
// returns the DEBUG_S_TYPE_MDTOKEN_MAP subsection as a byte array // DEBUG_S_TYPE_MDTOKEN_MAP subsection contains type-index to mdToken mapping // // contents of subsection: // offset 0, 4 bytes: count of entries in the map // offset 4, 8*N bytes: 4 byte type-index + 4 byte 'offset' relative to the start of 'type data' // offset 4+8*N, * bytes: ECMA formatted type signature packed sequentially with no padding // // 'offset' optimization: for type signatures with size<= 4-bytes // we can store the signature in offset field such that // offset = (1 << 31) | (sig[0] << 24 | sig[1] << 16 | sig[2] << 8 | sig[3]) // We chose this bit encoding because sig[0] holds the CorElementType whose // highest bit is always 0 and the highest bit of offset can be used as a flag // to indicate that it is not an offset but the signature itself. // // all entries are sorted by 'offset' field and so offset-based entries are arranged before other // (raw-signature) entries (since raw-signature entries are of the form 0x80000000 | signature, and will always be // numerically bigger than the offset) // private DebugInfoBlob GetDebugTypeIndexToTokenMap(ManagedBinaryEmitter pseudoAssembly, ICollection <KeyValuePair <TypeDesc, uint> > completeKnownTypes) { DebugInfoBlob typeDataBlob = new DebugInfoBlob(); DebugInfoBlob typeIndexToTokenMapBlob = new DebugInfoBlob(); List <KeyValuePair <uint, uint> > sigInOffsetEntries = new List <KeyValuePair <uint, uint> >(); typeIndexToTokenMapBlob.WriteDWORD(checked ((uint)completeKnownTypes.Count)); BlobBuilder blobBuilder = new BlobBuilder(); foreach (var entry in completeKnownTypes) { uint typeIndex = entry.Value; blobBuilder.Clear(); pseudoAssembly.EncodeSignatureForType(entry.Key, blobBuilder); // if signature fits in 4-bytes, store it in sigInOffsetEntries // otherwise store it in the type-data blob if (blobBuilder.Count <= 4) { uint sigInOffset = 0x80000000; int i = 0; // This is a slightly confusing approach, but this is how one iterates through the bytes in a blobBuilder without flattening it to a byte[] foreach (Blob blob in blobBuilder.GetBlobs()) { foreach (byte b in blob.GetBytes()) { sigInOffset |= ((uint)b) << (8 * (3 - i)); i++; } } // sigInOffsetEntries will be later sorted and appended to typeIndexToTokenMapBlob sigInOffsetEntries.Add(new KeyValuePair <uint, uint>(typeIndex, sigInOffset)); } else { typeIndexToTokenMapBlob.WriteDWORD(typeIndex); typeIndexToTokenMapBlob.WriteDWORD(typeDataBlob.Size()); typeDataBlob.WriteBuffer(blobBuilder); } } // sort sigInOffsetEntries based on sigInOffset sigInOffsetEntries.Sort((KeyValuePair <uint, uint> left, KeyValuePair <uint, uint> right) => { if (left.Value < right.Value) { return(-1); } if (left.Value == right.Value) { return(0); } return(1); }); // write the sorted sigInOffsetEntries foreach (KeyValuePair <uint, uint> sigInOffsetEntry in sigInOffsetEntries) { typeIndexToTokenMapBlob.WriteDWORD(sigInOffsetEntry.Key); typeIndexToTokenMapBlob.WriteDWORD(sigInOffsetEntry.Value); } // add typeDataBlob to the end of m_typeIndexToTokenMapBlob typeIndexToTokenMapBlob.WriteBuffer(typeDataBlob.ToArray()); return(typeIndexToTokenMapBlob); }
/// <summary> /// Serializes Portable PDB content into the given <see cref="BlobBuilder"/>. /// </summary> /// <param name="builder">Builder to write to.</param> /// <returns>The id of the serialized content.</returns> /// <exception cref="ArgumentNullException"><paramref name="builder"/> is null.</exception> public BlobContentId Serialize(BlobBuilder builder) { if (builder == null) { Throw.ArgumentNull(nameof(builder)); } // header: MetadataBuilder.SerializeMetadataHeader(builder, MetadataVersion, _serializedMetadata.Sizes); // #Pdb stream SerializeStandalonePdbStream(builder); // #~ or #- stream: _builder.SerializeMetadataTables(builder, _serializedMetadata.Sizes, _serializedMetadata.StringMap, methodBodyStreamRva: 0, mappedFieldDataStreamRva: 0); // #Strings, #US, #Guid and #Blob streams: _builder.WriteHeapsTo(builder, _serializedMetadata.StringHeap); var contentId = IdProvider(builder.GetBlobs()); // fill in the id: var idWriter = new BlobWriter(_pdbIdBlob); idWriter.WriteGuid(contentId.Guid); idWriter.WriteUInt32(contentId.Stamp); Debug.Assert(idWriter.RemainingBytes == 0); return contentId; }
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); } } }
/// <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; } } }
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()); }
/// <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);