using System; using System.Collections.Generic; using System.Linq; using System.Reflection.PortableExecutable; using Microsoft.DiaSymReader; using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata; using System.IO; using System.Reflection; namespace Semmle.Extraction.PDB { /// /// A PDB reader using Microsoft.DiaSymReader.Native. /// This is an unmanaged Windows DLL, which therefore only works on Windows. /// class NativePdbReader : IPdb { sealed class Document : ISourceFile { readonly ISymUnmanagedDocument document; public Document(ISymUnmanagedDocument doc) { document = doc; contents = new Lazy(() => { bool isEmbedded; if (document.HasEmbeddedSource(out isEmbedded) == 0 && isEmbedded) { var rawContents = document.GetEmbeddedSource().ToArray(); return System.Text.Encoding.Default.GetString(rawContents); } else { return File.Exists(Path) ? File.ReadAllText(Path) : null; } }); } public override bool Equals(object obj) { var otherDoc = obj as Document; return otherDoc != null && Path.Equals(otherDoc.Path); } public override int GetHashCode() => Path.GetHashCode(); public string Path => document.GetName(); public override string ToString() => Path; readonly Lazy contents; public string Contents => contents.Value; } public IEnumerable SourceFiles => reader.GetDocuments().Select(d => new Document(d)); public IMethod GetMethod(MethodDebugInformationHandle h) { int methodToken = MetadataTokens.GetToken(h.ToDefinitionHandle()); var method = reader.GetMethod(methodToken); if (method != null) { int count; if (method.GetSequencePointCount(out count) != 0 || count == 0) return null; var s = method.GetSequencePoints(). Where(sp => !sp.IsHidden). Select(sp => new SequencePoint(sp.Offset, new Location(new Document(sp.Document), sp.StartLine, sp.StartColumn, sp.EndLine, sp.EndColumn))). ToArray(); return s.Any() ? new Method { SequencePoints = s } : null; } return null; } NativePdbReader(string path) { pdbStream = new FileStream(path, FileMode.Open); var metadataProvider = new MdProvider(); reader = SymUnmanagedReaderFactory.CreateReader(pdbStream, metadataProvider); } readonly ISymUnmanagedReader5 reader; readonly FileStream pdbStream; public static NativePdbReader CreateFromAssembly(string assemblyPath, PEReader peReader) { // The Native PDB reader uses an unmanaged Windows DLL // so only works on Windows. if (!Semmle.Util.Win32.IsWindows()) return null; var debugDirectory = peReader.ReadDebugDirectory(); foreach (var path in debugDirectory. Where(d => d.Type == DebugDirectoryEntryType.CodeView). Select(peReader.ReadCodeViewDebugDirectoryData). Select(cv => cv.Path). Where(path => File.Exists(path))) { return new NativePdbReader(path); } return null; } public void Dispose() { pdbStream.Dispose(); } } /// /// This is not used but is seemingly needed in order to use DiaSymReader. /// class MdProvider : ISymReaderMetadataProvider { public MdProvider() { } public object GetMetadataImport() => null; public unsafe bool TryGetStandaloneSignature(int standaloneSignatureToken, out byte* signature, out int length) => throw new NotImplementedException(); public bool TryGetTypeDefinitionInfo(int typeDefinitionToken, out string namespaceName, out string typeName, out TypeAttributes attributes, out int baseTypeToken) => throw new NotImplementedException(); public bool TryGetTypeDefinitionInfo(int typeDefinitionToken, out string namespaceName, out string typeName, out TypeAttributes attributes) => throw new NotImplementedException(); public bool TryGetTypeReferenceInfo(int typeReferenceToken, out string namespaceName, out string typeName, out int resolutionScopeToken) => throw new NotImplementedException(); public bool TryGetTypeReferenceInfo(int typeReferenceToken, out string namespaceName, out string typeName) => throw new NotImplementedException(); } }