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;
        }
        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);
        }
Beispiel #3
0
        /// <exception cref="ArgumentNullException"><paramref name="destination"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Content is not available, the builder has been linked with another one.</exception>
        public void WriteContentTo(BlobBuilder destination)
        {
            if (destination == null)
            {
                Throw.ArgumentNull(nameof(destination));
            }

            foreach (var chunk in GetChunks())
            {
                destination.WriteBytes(chunk._buffer, 0, chunk.Length);
            }
        }
        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);
        }
 /// <summary>
 /// Write one entry in the "Debug Directory (Image Only)"
 /// See https://msdn.microsoft.com/en-us/windows/hardware/gg463119.aspx
 /// section 5.1.1 (pages 71-72).
 /// </summary>
 private static void WriteDebugTableEntry(
     BlobBuilder writer,
     byte[] stamp,
     uint version, // major and minor version, combined
     uint debugType,
     uint sizeOfData,
     uint addressOfRawData,
     uint pointerToRawData)
 {
     writer.WriteUInt32(0); // characteristics
     Debug.Assert(stamp.Length == 4);
     writer.WriteBytes(stamp);
     writer.WriteUInt32(version);
     writer.WriteUInt32(debugType);
     writer.WriteUInt32(sizeOfData);
     writer.WriteUInt32(addressOfRawData);
     writer.WriteUInt32(pointerToRawData);
 }
Beispiel #6
0
        public void TryOpenAssociatedPortablePdb_BadStreamProvider()
        {
            var pdbBuilder = new BlobBuilder();
            pdbBuilder.WriteBytes(PortablePdbs.DocumentsPdb);

            var id = new BlobContentId(Guid.Parse("18091B06-32BB-46C2-9C3B-7C9389A2F6C6"), 0x12345678);
            var ddBuilder = new DebugDirectoryBuilder();
            ddBuilder.AddCodeViewEntry(@"/a/b/a.pdb", id, portablePdbVersion: 0x0100);

            var peStream = new MemoryStream(TestBuilders.BuildPEWithDebugDirectory(ddBuilder));

            using (var reader = new PEReader(peStream))
            {
                MetadataReaderProvider pdbProvider;
                string pdbPath;

                // pass-thru:
                Assert.Throws<ArgumentException>(() =>
                    reader.TryOpenAssociatedPortablePdb(Path.Combine("pedir", "file.exe"), _ => { throw new ArgumentException(); }, out pdbProvider, out pdbPath));

                Assert.Throws<InvalidOperationException>(() =>
                    reader.TryOpenAssociatedPortablePdb(Path.Combine("pedir", "file.exe"), _ => { return new TestStream(canRead: false, canWrite: true, canSeek: true); }, out pdbProvider, out pdbPath));

                Assert.Throws<InvalidOperationException>(() =>
                    reader.TryOpenAssociatedPortablePdb(Path.Combine("pedir", "file.exe"), _ => { return new TestStream(canRead: true, canWrite: true, canSeek: false); }, out pdbProvider, out pdbPath));
            }
        }
        /// <summary>
        /// Serializes .text section data into a specified <paramref name="builder"/>.
        /// </summary>
        /// <param name="builder">An empty builder to serialize section data to.</param>
        /// <param name="relativeVirtualAddess">Relative virtual address of the section within the containing PE file.</param>
        /// <param name="entryPointTokenOrRelativeVirtualAddress">Entry point token or RVA (<see cref="CorHeader.EntryPointTokenOrRelativeVirtualAddress"/>)</param>
        /// <param name="corFlags">COR Flags (<see cref="CorHeader.Flags"/>).</param>
        /// <param name="baseAddress">Base address of the PE image.</param>
        /// <param name="metadataBuilder"><see cref="BlobBuilder"/> containing metadata. Must be populated with data. Linked into the <paramref name="builder"/> and can't be expanded afterwards.</param>
        /// <param name="ilBuilder"><see cref="BlobBuilder"/> containing IL stream. Must be populated with data. Linked into the <paramref name="builder"/> and can't be expanded afterwards.</param>
        /// <param name="mappedFieldDataBuilder"><see cref="BlobBuilder"/> containing mapped field data. Must be populated with data. Linked into the <paramref name="builder"/> and can't be expanded afterwards.</param>
        /// <param name="resourceBuilder"><see cref="BlobBuilder"/> containing managed resource data. Must be populated with data. Linked into the <paramref name="builder"/> and can't be expanded afterwards.</param>
        /// <param name="debugTableBuilderOpt"><see cref="BlobBuilder"/> containing debug table data. Must be populated with data. Linked into the <paramref name="builder"/> and can't be expanded afterwards.</param>
        public void Serialize(
            BlobBuilder builder,
            int relativeVirtualAddess,
            int entryPointTokenOrRelativeVirtualAddress,
            CorFlags corFlags,
            ulong baseAddress,
            BlobBuilder metadataBuilder,
            BlobBuilder ilBuilder,
            BlobBuilder mappedFieldDataBuilder,
            BlobBuilder resourceBuilder,
            BlobBuilder debugTableBuilderOpt)
        {
            Debug.Assert(builder.Count == 0);
            Debug.Assert(metadataBuilder.Count == MetadataSize);
            Debug.Assert(metadataBuilder.Count % 4 == 0);
            Debug.Assert(ilBuilder.Count == ILStreamSize);
            Debug.Assert(mappedFieldDataBuilder.Count == MappedFieldDataSize);
            Debug.Assert(resourceBuilder.Count == ResourceDataSize);
            Debug.Assert(resourceBuilder.Count % 4 == 0);

            // TODO: avoid recalculation
            int importTableRva = GetImportTableDirectoryEntry(relativeVirtualAddess).RelativeVirtualAddress;
            int importAddressTableRva = GetImportAddressTableDirectoryEntry(relativeVirtualAddess).RelativeVirtualAddress;

            if (RequiresStartupStub)
            {
                WriteImportAddressTable(builder, importTableRva);
            }

            WriteCorHeader(builder, relativeVirtualAddess, entryPointTokenOrRelativeVirtualAddress, corFlags);

            // IL:
            ilBuilder.Align(4);
            builder.LinkSuffix(ilBuilder);

            // metadata:
            builder.LinkSuffix(metadataBuilder);

            // managed resources:
            builder.LinkSuffix(resourceBuilder);

            // strong name signature:
            builder.WriteBytes(0, StrongNameSignatureSize);

            if (debugTableBuilderOpt != null)
            {
                builder.LinkSuffix(debugTableBuilderOpt);
            }

            if (RequiresStartupStub)
            {
                WriteImportTable(builder, importTableRva, importAddressTableRva);
                WriteNameTable(builder);
                WriteRuntimeStartupStub(builder, importAddressTableRva, baseAddress);
            }

            // mapped field data:            
            builder.LinkSuffix(mappedFieldDataBuilder);

            Debug.Assert(builder.Count == ComputeSizeOfTextSection());
        }
Beispiel #8
0
        public void TryOpenAssociatedPortablePdb_ExpectedExceptionFromStreamProvider_FallbackOnEmbedded_Valid()
        {
            var pdbBuilder = new BlobBuilder();
            pdbBuilder.WriteBytes(PortablePdbs.DocumentsPdb);

            var id = new BlobContentId(Guid.Parse("18091B06-32BB-46C2-9C3B-7C9389A2F6C6"), 0x12345678);
            var ddBuilder = new DebugDirectoryBuilder();
            ddBuilder.AddCodeViewEntry(@"/a/b/a.pdb", id, portablePdbVersion: 0x0100);
            ddBuilder.AddEmbeddedPortablePdbEntry(pdbBuilder, portablePdbVersion: 0x0100);

            var peStream = new MemoryStream(TestBuilders.BuildPEWithDebugDirectory(ddBuilder));

            using (var reader = new PEReader(peStream))
            {
                MetadataReaderProvider pdbProvider;
                string pdbPath;

                Assert.True(reader.TryOpenAssociatedPortablePdb(Path.Combine("pedir", "file.exe"), _ => { throw new IOException(); }, out pdbProvider, out pdbPath));
                Assert.Null(pdbPath);
                Assert.Equal(13, pdbProvider.GetMetadataReader().Documents.Count);

                Assert.True(reader.TryOpenAssociatedPortablePdb(Path.Combine("pedir", "file.exe"), _ => { throw new BadImageFormatException(); }, out pdbProvider, out pdbPath));
                Assert.Null(pdbPath);
                Assert.Equal(13, pdbProvider.GetMetadataReader().Documents.Count);

                Assert.True(reader.TryOpenAssociatedPortablePdb(Path.Combine("pedir", "file.exe"), _ => { throw new FileNotFoundException(); }, out pdbProvider, out pdbPath));
                Assert.Null(pdbPath);
                Assert.Equal(13, pdbProvider.GetMetadataReader().Documents.Count);
            }
        }
Beispiel #9
0
        public void TryOpenAssociatedPortablePdb_ExpectedExceptionFromStreamProvider_NoFallback()
        {
            var pdbBuilder = new BlobBuilder();
            pdbBuilder.WriteBytes(PortablePdbs.DocumentsPdb);

            var id = new BlobContentId(Guid.Parse("18091B06-32BB-46C2-9C3B-7C9389A2F6C6"), 0x12345678);
            var ddBuilder = new DebugDirectoryBuilder();
            ddBuilder.AddCodeViewEntry(@"/a/b/a.pdb", id, portablePdbVersion: 0x0100);

            var peStream = new MemoryStream(TestBuilders.BuildPEWithDebugDirectory(ddBuilder));

            using (var reader = new PEReader(peStream))
            {
                MetadataReaderProvider pdbProvider;
                string pdbPath;

                Assert.Throws<IOException>(() =>
                    reader.TryOpenAssociatedPortablePdb(Path.Combine("pedir", "file.exe"), _ => { throw new IOException(); }, out pdbProvider, out pdbPath));

                AssertEx.Throws<BadImageFormatException>(() =>
                    reader.TryOpenAssociatedPortablePdb(Path.Combine("pedir", "file.exe"), _ => { throw new BadImageFormatException("Bang!"); }, out pdbProvider, out pdbPath),
                    e => Assert.Equal("Bang!", e.Message));

                // file doesn't exist and no embedded => return false
                Assert.False(reader.TryOpenAssociatedPortablePdb(Path.Combine("pedir", "file.exe"), _ => { throw new FileNotFoundException(); }, out pdbProvider, out pdbPath));
            }
        }
Beispiel #10
0
        public void TryOpenAssociatedPortablePdb_BadPdbFile_FallbackToEmbedded()
        {
            var pdbBuilder = new BlobBuilder();
            pdbBuilder.WriteBytes(PortablePdbs.DocumentsPdb);

            var id = new BlobContentId(Guid.Parse("18091B06-32BB-46C2-9C3B-7C9389A2F6C6"), 0x12345678);
            var ddBuilder = new DebugDirectoryBuilder();
            ddBuilder.AddCodeViewEntry(@"/a/b/a.pdb", id, portablePdbVersion: 0x0100);
            ddBuilder.AddEmbeddedPortablePdbEntry(pdbBuilder, portablePdbVersion: 0x0100);

            var peStream = new MemoryStream(TestBuilders.BuildPEWithDebugDirectory(ddBuilder));

            using (var reader = new PEReader(peStream))
            {
                string pathQueried = null;

                Func<string, Stream> streamProvider = p =>
                {
                    Assert.Null(pathQueried);
                    pathQueried = p;

                    // Bad PDB
                    return new MemoryStream(new byte[] { 0x01 });
                };

                MetadataReaderProvider pdbProvider;
                string pdbPath;
                Assert.True(reader.TryOpenAssociatedPortablePdb(Path.Combine("pedir", "file.exe"), streamProvider, out pdbProvider, out pdbPath));

                Assert.Null(pdbPath);
                Assert.Equal(PathUtilities.CombinePathWithRelativePath("pedir", "a.pdb"), pathQueried);

                Assert.Equal(13, pdbProvider.GetMetadataReader().Documents.Count);
            }
        }
Beispiel #11
0
        private void WritePESignature(BlobBuilder builder)
        {
            // MS-DOS stub (128 bytes)
            builder.WriteBytes(s_dosHeader);

            // PE Signature "PE\0\0" 
            builder.WriteUInt32(0x00004550);
        }
Beispiel #12
0
        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 #13
0
        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)));
        }
Beispiel #14
0
        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)));
        }