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); }
/// <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); }
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()); }
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); } }
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)); } }
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); } }
private void WritePESignature(BlobBuilder builder) { // MS-DOS stub (128 bytes) builder.WriteBytes(s_dosHeader); // PE Signature "PE\0\0" builder.WriteUInt32(0x00004550); }
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))); }
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_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))); }