Beispiel #1
0
        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)));
        }
Beispiel #5
0
        /// <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);
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        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));
                }
            }
        }
Beispiel #8
0
 internal static void AppendData(this IncrementalHash hash, BlobBuilder builder)
 {
     foreach (var blob in builder.GetBlobs())
     {
         hash.AppendData(blob.GetBytes());
     }
 }
Beispiel #9
0
        // 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;
                    }
                }
            }
        }
Beispiel #10
0
        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());
            }
        }
Beispiel #11
0
        /// <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();
        }
Beispiel #12
0
        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})");
            }
        }
Beispiel #14
0
        // 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);
                }
            }
        }
Beispiel #15
0
        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());
        }
Beispiel #16
0
        public void Link()
        {
            var builder1 = new BlobBuilder(16);

            builder1.WriteByte(1);

            var builder2 = new BlobBuilder(16);

            builder2.WriteByte(2);

            var builder3 = new BlobBuilder(16);

            builder3.WriteByte(3);

            var builder4 = new BlobBuilder(16);

            builder4.WriteByte(4);

            var builder5 = new BlobBuilder(16);

            builder5.WriteByte(5);

            builder2.LinkPrefix(builder1);
            AssertEx.Equal(new byte[] { 1, 2 }, builder2.ToArray());
            Assert.Throws <InvalidOperationException>(() => builder1.ToArray());
            Assert.Throws <InvalidOperationException>(() => builder2.LinkPrefix(builder1));
            Assert.Throws <InvalidOperationException>(() => builder1.WriteByte(0xff));
            Assert.Throws <InvalidOperationException>(() => builder1.WriteBytes(1, 10));
            Assert.Throws <InvalidOperationException>(() => builder1.WriteBytes(new byte[] { 1 }));
            Assert.Throws <InvalidOperationException>(() => builder1.ReserveBytes(1));
            Assert.Throws <InvalidOperationException>(() => builder1.GetBlobs());
            Assert.Throws <InvalidOperationException>(() => builder1.ContentEquals(builder1));
            Assert.Throws <InvalidOperationException>(() => builder1.WriteUTF16("str"));
            Assert.Throws <InvalidOperationException>(() => builder1.WriteUTF8("str", allowUnpairedSurrogates: false));

            builder2.LinkSuffix(builder3);
            AssertEx.Equal(new byte[] { 1, 2, 3 }, builder2.ToArray());
            Assert.Throws <InvalidOperationException>(() => builder3.LinkPrefix(builder5));

            builder2.LinkPrefix(builder4);
            AssertEx.Equal(new byte[] { 4, 1, 2, 3 }, builder2.ToArray());
            Assert.Throws <InvalidOperationException>(() => builder4.LinkPrefix(builder5));

            builder2.LinkSuffix(builder5);
            AssertEx.Equal(new byte[] { 4, 1, 2, 3, 5 }, builder2.ToArray());
        }
        public 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);
        }
Beispiel #19
0
        /// <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);
        }
Beispiel #20
0
        /// <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;
            }
        }
Beispiel #21
0
        // 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;
        }
Beispiel #23
0
        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);
                }
            }
        }
Beispiel #24
0
        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);
        }
Beispiel #25
0
        /// <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;
                }
            }
        }
Beispiel #26
0
        public void Link()
        {
            var builder1 = new BlobBuilder(16);
            builder1.WriteByte(1);

            var builder2 = new BlobBuilder(16);
            builder2.WriteByte(2);

            var builder3 = new BlobBuilder(16);
            builder3.WriteByte(3);

            var builder4 = new BlobBuilder(16);
            builder4.WriteByte(4);

            var builder5 = new BlobBuilder(16);
            builder5.WriteByte(5);

            builder2.LinkPrefix(builder1);
            AssertEx.Equal(new byte[] { 1, 2 }, builder2.ToArray());
            Assert.Throws<InvalidOperationException>(() => builder1.ToArray());
            Assert.Throws<InvalidOperationException>(() => builder2.LinkPrefix(builder1));
            Assert.Throws<InvalidOperationException>(() => builder1.WriteByte(0xff));
            Assert.Throws<InvalidOperationException>(() => builder1.WriteBytes(1, 10));
            Assert.Throws<InvalidOperationException>(() => builder1.WriteBytes(new byte[] { 1 }));
            Assert.Throws<InvalidOperationException>(() => builder1.ReserveBytes(1));
            Assert.Throws<InvalidOperationException>(() => builder1.GetBlobs());
            Assert.Throws<InvalidOperationException>(() => builder1.ContentEquals(builder1));
            Assert.Throws<InvalidOperationException>(() => builder1.WriteUTF16("str"));
            Assert.Throws<InvalidOperationException>(() => builder1.WriteUTF8("str", allowUnpairedSurrogates: false));

            builder2.LinkSuffix(builder3);
            AssertEx.Equal(new byte[] { 1, 2, 3 }, builder2.ToArray());
            Assert.Throws<InvalidOperationException>(() => builder3.LinkPrefix(builder5));

            builder2.LinkPrefix(builder4);
            AssertEx.Equal(new byte[] { 4, 1, 2, 3 }, builder2.ToArray());
            Assert.Throws<InvalidOperationException>(() => builder4.LinkPrefix(builder5));

            builder2.LinkSuffix(builder5);
            AssertEx.Equal(new byte[] { 4, 1, 2, 3, 5 }, builder2.ToArray());
        }
Beispiel #27
0
        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());
        }
Beispiel #28
0
        /// <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);
Beispiel #29
0
        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);
                }
            }
        }
Beispiel #30
0
        /// <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;
                }
            }
        }