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); } }
private static OpenedReader TryOpenReaderFromCodeView(PEReader peReader, DebugDirectoryEntry codeViewEntry, string assemblyPath) { OpenedReader result = null; MetadataReaderProvider provider = null; try { var data = peReader.ReadCodeViewDebugDirectoryData(codeViewEntry); string pdbPath = data.Path; if (assemblyPath != null) { try { pdbPath = Path.Combine(Path.GetDirectoryName(assemblyPath), Path.GetFileName(pdbPath)); } catch { // invalid characters in CodeView path return null; } } var pdbStream = TryOpenFile(pdbPath); if (pdbStream == null) { return null; } provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream); var reader = provider.GetMetadataReader(); // Validate that the PDB matches the assembly version if (data.Age == 1 && new BlobContentId(reader.DebugMetadataHeader.Id) == new BlobContentId(data.Guid, codeViewEntry.Stamp)) { result = new OpenedReader(provider, reader); } } catch (Exception e) when (e is BadImageFormatException || e is IOException) { return null; } finally { if (result == null) { provider?.Dispose(); } } return result; }
/// <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 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; }