public void EmbeddedPortablePdb() { string source = @" using System; class C { public static void Main() { Console.WriteLine(); } } "; var c = CreateCompilationWithMscorlib(Parse(source, "foo.cs"), options: TestOptions.DebugDll); var peBlob = c.EmitToArray(EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded).WithPdbFilePath(@"a/b/c/d.pdb")); using (var peReader = new PEReader(peBlob)) { var entries = peReader.ReadDebugDirectory(); AssertEx.Equal(new[] { DebugDirectoryEntryType.CodeView, DebugDirectoryEntryType.EmbeddedPortablePdb }, entries.Select(e => e.Type)); var codeView = entries[0]; var embedded = entries[1]; // EmbeddedPortablePdb entry: Assert.Equal(0x0100, embedded.MajorVersion); Assert.Equal(0x0100, embedded.MinorVersion); Assert.Equal(0u, embedded.Stamp); BlobContentId pdbId; using (var embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embedded)) { var mdReader = embeddedMetadataProvider.GetMetadataReader(); AssertEx.Equal(new[] { "foo.cs" }, mdReader.Documents.Select(doc => mdReader.GetString(mdReader.GetDocument(doc).Name))); pdbId = new BlobContentId(mdReader.DebugMetadataHeader.Id); } // CodeView entry: var codeViewData = peReader.ReadCodeViewDebugDirectoryData(codeView); Assert.Equal(0x0100, codeView.MajorVersion); Assert.Equal(0x504D, codeView.MinorVersion); Assert.Equal(pdbId.Stamp, codeView.Stamp); Assert.Equal(pdbId.Guid, codeViewData.Guid); Assert.Equal("d.pdb", codeViewData.Path); } }
/// <summary> /// Returns metadata readers for assembly PE file and portable PDB. /// </summary> /// <param name="assemblyFileName">file name of the assembly</param> /// <param name="peReader">PE metadata reader return</param> /// <param name="pdbReader">PDB metadata reader return</param> /// <returns>true if debugging information is available</returns> private static bool GetReaders(string assemblyFileName, out MetadataReader peReader, out MetadataReader pdbReader) { peReader = null; pdbReader = null; if (!File.Exists(assemblyFileName)) { return false; } Stream peStream = File.OpenRead(assemblyFileName); PEReader reader = new PEReader(peStream); string pdbPath = null; foreach (DebugDirectoryEntry entry in reader.ReadDebugDirectory()) { if (entry.Type == DebugDirectoryEntryType.CodeView) { CodeViewDebugDirectoryData codeViewData = reader.ReadCodeViewDebugDirectoryData(entry); pdbPath = codeViewData.Path; break; } } if (pdbPath == null) { return false; } if (!File.Exists(pdbPath)) { pdbPath = Path.GetFileName(pdbPath); if (!File.Exists(pdbPath)) { return false; } } peReader = reader.GetMetadataReader(); Stream pdbStream = File.OpenRead(pdbPath); MetadataReaderProvider provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream); pdbReader = provider.GetMetadataReader(); return true; }
private static void ReadPortableDebugTableEntries(PEReader peReader, out DebugDirectoryEntry codeViewEntry, out DebugDirectoryEntry embeddedPdbEntry) { // See spec: https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md codeViewEntry = default(DebugDirectoryEntry); embeddedPdbEntry = default(DebugDirectoryEntry); foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory()) { if (entry.Type == DebugDirectoryEntryType.CodeView) { const ushort PortableCodeViewVersionMagic = 0x504d; if (entry.MinorVersion != PortableCodeViewVersionMagic) { continue; } codeViewEntry = entry; } else if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb) { embeddedPdbEntry = entry; } } }
public void EmbeddedSource_InEmbeddedPdb() { string source = @" using System; class C { public static void Main() { Console.WriteLine(); } } "; var tree = Parse(source, "f:/build/foo.cs"); var c = CreateCompilationWithMscorlib(tree, options: TestOptions.DebugDll); var pdbStream = new MemoryStream(); var peBlob = c.EmitToArray( EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded), embeddedTexts: new[] { EmbeddedText.FromSource(tree.FilePath, tree.GetText()) }); pdbStream.Position = 0; using (var peReader = new PEReader(peBlob)) { var embeddedEntry = peReader.ReadDebugDirectory().Single(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb); using (var embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedEntry)) { var pdbReader = embeddedMetadataProvider.GetMetadataReader(); var embeddedSource = (from documentHandle in pdbReader.Documents let document = pdbReader.GetDocument(documentHandle) select new { FilePath = pdbReader.GetString(document.Name), Text = pdbReader.GetEmbeddedSource(documentHandle) }).Single(); Assert.Equal(embeddedSource.FilePath, "f:/build/foo.cs"); Assert.Equal(source, embeddedSource.Text.ToString()); } } }
public void SourceLink_Embedded() { string source = @" using System; class C { public static void Main() { Console.WriteLine(); } } "; var sourceLinkBlob = Encoding.UTF8.GetBytes(@" { ""documents"": { ""f:/build/*"" : ""https://raw.githubusercontent.com/my-org/my-project/1111111111111111111111111111111111111111/*"" } } "); var c = CreateCompilationWithMscorlib(Parse(source, "f:/build/foo.cs"), options: TestOptions.DebugDll); var peBlob = c.EmitToArray(EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded), sourceLinkStream: new MemoryStream(sourceLinkBlob)); using (var peReader = new PEReader(peBlob)) { var embeddedEntry = peReader.ReadDebugDirectory().Single(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb); using (var embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedEntry)) { var pdbReader = embeddedMetadataProvider.GetMetadataReader(); var actualBlob = (from cdiHandle in pdbReader.GetCustomDebugInformation(EntityHandle.ModuleDefinition) let cdi = pdbReader.GetCustomDebugInformation(cdiHandle) where pdbReader.GetGuid(cdi.Kind) == PortableCustomDebugInfoKinds.SourceLink select pdbReader.GetBlobBytes(cdi.Value)).Single(); AssertEx.Equal(sourceLinkBlob, actualBlob); } } }
private bool TryReadCodeViewData(string peFilePath, out CodeViewDebugDirectoryData codeViewData, out uint stamp) { try { var peStream = PortableShim.File.OpenRead(peFilePath); using (var peReader = new PEReader(peStream)) { foreach (var entry in peReader.ReadDebugDirectory()) { if (entry.Type == DebugDirectoryEntryType.CodeView) { codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry); stamp = entry.Stamp; return true; } } } } catch { } codeViewData = default(CodeViewDebugDirectoryData); stamp = 0; return false; }
/// <summary> /// Read the pdb file name and assembly version information from the PE file. /// </summary> /// <param name="assemblyPath">PE file path</param> /// <param name="loadedPeAddress">loaded PE image address or zero</param> /// <param name="loadedPeSize">loaded PE image size</param> /// <param name="age">age</param> /// <param name="guid">assembly guid</param> /// <param name="stamp">time stamp</param> /// <returns>pdb name or null</returns> /// <remarks> /// loadedPeAddress and loadedPeSize will be used here when/if the PEReader /// support runtime loaded images instead of just file images. /// </remarks> private static unsafe string GetPdbPathFromPeStream(string assemblyPath, IntPtr loadedPeAddress, int loadedPeSize, out int age, out Guid guid, out uint stamp) { if (File.Exists(assemblyPath)) { Stream peStream = File.OpenRead(assemblyPath); using (PEReader peReader = new PEReader(peStream)) { foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory()) { if (entry.Type == DebugDirectoryEntryType.CodeView) { CodeViewDebugDirectoryData codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry); stamp = entry.Stamp; age = codeViewData.Age; guid = codeViewData.Guid; string peDirectory = Path.GetDirectoryName(assemblyPath); return Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path)); } } } } stamp = 0; age = 0; guid = new Guid(); return null; }