/// <summary>
 /// Checks availability of debugging information for given assembly.
 /// </summary>
 /// <param name="assemblyPath">
 /// File path of the assembly or null
 /// </param>
 /// <param name="isFileLayout">type of in-memory PE layout, if true, file based layout otherwise, loaded layout</param>
 /// <param name="loadedPeAddress">
 /// Loaded PE image address or zero if the module is dynamic (generated by Reflection.Emit).
 /// Dynamic modules have their PDBs (if any) generated to an in-memory stream
 /// (pointed to by <paramref name="inMemoryPdbAddress"/> and <paramref name="inMemoryPdbSize"/>).
 /// </param>
 /// <param name="loadedPeSize">loaded PE image size</param>
 /// <param name="inMemoryPdbAddress">in memory PDB address or zero</param>
 /// <param name="inMemoryPdbSize">in memory PDB size</param>
 /// <returns>Symbol reader handle or zero if error</returns>
 private IntPtr LoadSymbolsForModule(
     IntPtr self,
     string assemblyPath,
     bool isFileLayout,
     ulong loadedPeAddress,
     uint loadedPeSize,
     ulong inMemoryPdbAddress,
     uint inMemoryPdbSize)
 {
     try
     {
         Stream peStream = null;
         if (loadedPeAddress != 0)
         {
             peStream = MemoryService.CreateMemoryStream(loadedPeAddress, loadedPeSize);
         }
         Stream pdbStream = null;
         if (inMemoryPdbAddress != 0)
         {
             pdbStream = MemoryService.CreateMemoryStream(inMemoryPdbAddress, inMemoryPdbSize);
         }
         OpenedReader openedReader = GetReader(assemblyPath, isFileLayout, peStream, pdbStream);
         if (openedReader != null)
         {
             GCHandle gch = GCHandle.Alloc(openedReader);
             return(GCHandle.ToIntPtr(gch));
         }
     }
     catch (Exception ex)
     {
         Trace.TraceError($"LoadSymbolsForModule: {ex.Message}");
     }
     return(IntPtr.Zero);
 }
        private OpenedReader TryOpenReaderFromEmbeddedPdb(PEReader peReader, DebugDirectoryEntry embeddedPdbEntry)
        {
            OpenedReader           result   = null;
            MetadataReaderProvider provider = null;

            try
            {
                // TODO: We might want to cache this provider globally (across stack traces),
                // since decompressing embedded PDB takes some time.
                provider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry);
                result   = new OpenedReader(provider, provider.GetMetadataReader());
            }
            catch (Exception e) when(e is BadImageFormatException || e is IOException)
            {
                return(null);
            }
            finally
            {
                if (result == null)
                {
                    provider?.Dispose();
                }
            }

            return(result);
        }
Пример #3
0
 /// <summary>
 /// Checks availability of debugging information for given assembly.
 /// </summary>
 /// <param name="assemblyPath">
 /// File path of the assembly or null if the module is in-memory or dynamic (generated by Reflection.Emit)
 /// </param>
 /// <param name="isFileLayout">type of in-memory PE layout, if true, file based layout otherwise, loaded layout</param>
 /// <param name="loadedPeAddress">
 /// Loaded PE image address or zero if the module is dynamic (generated by Reflection.Emit).
 /// Dynamic modules have their PDBs (if any) generated to an in-memory stream
 /// (pointed to by <paramref name="inMemoryPdbAddress"/> and <paramref name="inMemoryPdbSize"/>).
 /// </param>
 /// <param name="loadedPeSize">loaded PE image size</param>
 /// <param name="inMemoryPdbAddress">in memory PDB address or zero</param>
 /// <param name="inMemoryPdbSize">in memory PDB size</param>
 /// <returns>Symbol reader handle or zero if error</returns>
 internal static IntPtr LoadSymbolsForModule(string assemblyPath, bool isFileLayout, ulong loadedPeAddress, int loadedPeSize,
                                             ulong inMemoryPdbAddress, int inMemoryPdbSize, ReadMemoryDelegate readMemory)
 {
     try
     {
         TargetStream peStream = null;
         if (assemblyPath == null && loadedPeAddress != 0)
         {
             peStream = new TargetStream(loadedPeAddress, loadedPeSize, readMemory);
         }
         TargetStream pdbStream = null;
         if (inMemoryPdbAddress != 0)
         {
             pdbStream = new TargetStream(inMemoryPdbAddress, inMemoryPdbSize, readMemory);
         }
         OpenedReader openedReader = GetReader(assemblyPath, isFileLayout, peStream, pdbStream);
         if (openedReader != null)
         {
             GCHandle gch = GCHandle.Alloc(openedReader);
             return(GCHandle.ToIntPtr(gch));
         }
     }
     catch
     {
     }
     return(IntPtr.Zero);
 }
        private unsafe static OpenedReader TryOpenReaderForInMemoryPdb(IntPtr inMemoryPdbAddress, int inMemoryPdbSize)
        {
            Debug.Assert(inMemoryPdbAddress != IntPtr.Zero);

            // quick check to avoid throwing exceptions below in common cases:
            const uint ManagedMetadataSignature = 0x424A5342;

            if (inMemoryPdbSize < sizeof(uint) || *(uint *)inMemoryPdbAddress != ManagedMetadataSignature)
            {
                // not a Portable PDB
                return(null);
            }

            OpenedReader           result   = null;
            MetadataReaderProvider provider = null;

            try
            {
                provider = MetadataReaderProvider.FromMetadataImage((byte *)inMemoryPdbAddress, inMemoryPdbSize);
                result   = new OpenedReader(provider, provider.GetMetadataReader());
            }
            catch (BadImageFormatException)
            {
                return(null);
            }
            finally
            {
                if (result == null)
                {
                    provider?.Dispose();
                }
            }

            return(result);
        }
Пример #5
0
        private static OpenedReader TryOpenReaderFromCodeView(PEReader peReader, DebugDirectoryEntry codeViewEntry, string assemblyPath)
        {
            OpenedReader           result   = null;
            MetadataReaderProvider provider = null;

            try
            {
                CodeViewDebugDirectoryData data = peReader.ReadCodeViewDebugDirectoryData(codeViewEntry);
                string pdbPath   = data.Path;
                Stream pdbStream = null;

                if (assemblyPath != null)
                {
                    try
                    {
                        pdbPath = Path.Combine(Path.GetDirectoryName(assemblyPath), GetFileName(pdbPath));
                    }
                    catch
                    {
                        // invalid characters in CodeView path
                        return(null);
                    }
                    pdbStream = TryOpenFile(pdbPath);
                }

                if (pdbStream == null)
                {
                    if (s_symbolStore == null)
                    {
                        return(null);
                    }
                    Debug.Assert(codeViewEntry.MinorVersion == ImageDebugDirectory.PortablePDBMinorVersion);
                    SymbolStoreKey key = PortablePDBFileKeyGenerator.GetKey(pdbPath, data.Guid);
                    pdbStream = GetSymbolStoreFile(key)?.Stream;
                }

                provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
                MetadataReader 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);
        }
Пример #6
0
        /// <summary>
        /// Helper method to return source line number and source file name for given IL offset and method token.
        /// </summary>
        /// <param name="openedReader">symbol reader returned by LoadSymbolsForModule</param>
        /// <param name="methodToken">method token</param>
        /// <param name="ilOffset">IL offset</param>
        /// <param name="lineNumber">source line number return</param>
        /// <param name="fileName">source file name return</param>
        /// <returns> true if information is available</returns>
        private bool GetSourceLineByILOffset(
            OpenedReader openedReader,
            int methodToken,
            long ilOffset,
            out int lineNumber,
            out string fileName)
        {
            lineNumber = 0;
            fileName   = null;
            MetadataReader reader = openedReader.Reader;

            try
            {
                Handle handle = MetadataTokens.Handle(methodToken);
                if (handle.Kind != HandleKind.MethodDefinition)
                {
                    return(false);
                }

                MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
                if (methodDebugHandle.IsNil)
                {
                    return(false);
                }

                MethodDebugInformation  methodDebugInfo = reader.GetMethodDebugInformation(methodDebugHandle);
                SequencePointCollection sequencePoints  = methodDebugInfo.GetSequencePoints();

                SequencePoint?nearestPoint = null;
                foreach (SequencePoint point in sequencePoints)
                {
                    if (point.Offset > ilOffset)
                    {
                        break;
                    }

                    if (point.StartLine != 0 && !point.IsHidden)
                    {
                        nearestPoint = point;
                    }
                }

                if (nearestPoint.HasValue)
                {
                    lineNumber = nearestPoint.Value.StartLine;
                    fileName   = reader.GetString(reader.GetDocument(nearestPoint.Value.Document).Name);
                    return(true);
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError($"GetSourceLineByILOffset: {ex.Message}");
            }
            return(false);
        }
Пример #7
0
        private static bool GetLocalsInfoForMethod(string assemblyPath, int methodToken, out List <LocalVarInfo> locals)
        {
            locals = null;

            OpenedReader openedReader = GetReader(assemblyPath, isFileLayout: true, peStream: null, pdbStream: null);

            if (openedReader == null)
            {
                return(false);
            }

            using (openedReader)
            {
                try
                {
                    Handle handle = MetadataTokens.Handle(methodToken);
                    if (handle.Kind != HandleKind.MethodDefinition)
                    {
                        return(false);
                    }

                    locals = new List <LocalVarInfo>();

                    MethodDebugInformationHandle methodDebugHandle =
                        ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
                    LocalScopeHandleCollection localScopes = openedReader.Reader.GetLocalScopes(methodDebugHandle);
                    foreach (LocalScopeHandle scopeHandle in localScopes)
                    {
                        LocalScope scope = openedReader.Reader.GetLocalScope(scopeHandle);
                        LocalVariableHandleCollection localVars = scope.GetLocalVariables();
                        foreach (LocalVariableHandle varHandle in localVars)
                        {
                            LocalVariable localVar = openedReader.Reader.GetLocalVariable(varHandle);
                            if (localVar.Attributes == LocalVariableAttributes.DebuggerHidden)
                            {
                                continue;
                            }
                            LocalVarInfo info = new LocalVarInfo();
                            info.startOffset = scope.StartOffset;
                            info.endOffset   = scope.EndOffset;
                            info.name        = openedReader.Reader.GetString(localVar.Name);
                            locals.Add(info);
                        }
                    }
                }
                catch
                {
                    return(false);
                }
            }
            return(true);
        }
Пример #8
0
        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);
        }
Пример #9
0
        /// <summary>
        /// Helper method to return source name, line numbers and IL offsets for given method token.
        /// </summary>
        /// <param name="assemblyPath">file path of the assembly</param>
        /// <param name="methodToken">method token</param>
        /// <param name="points">list of debug information for each sequence point return</param>
        /// <returns>true if information is available</returns>
        /// <remarks>used by the gdb JIT support (not SOS). Does not support in-memory PEs or PDBs</remarks>
        private static bool GetDebugInfoForMethod(string assemblyPath, int methodToken, out List <DebugInfo> points)
        {
            points = null;

            OpenedReader openedReader = GetReader(assemblyPath, isFileLayout: true, peStream: null, pdbStream: null);

            if (openedReader == null)
            {
                return(false);
            }

            using (openedReader)
            {
                try
                {
                    Handle handle = MetadataTokens.Handle(methodToken);
                    if (handle.Kind != HandleKind.MethodDefinition)
                    {
                        return(false);
                    }

                    points = new List <DebugInfo>();
                    MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
                    MethodDebugInformation       methodDebugInfo   = openedReader.Reader.GetMethodDebugInformation(methodDebugHandle);
                    SequencePointCollection      sequencePoints    = methodDebugInfo.GetSequencePoints();

                    foreach (SequencePoint point in sequencePoints)
                    {
                        if (point.StartLine == 0 || point.StartLine == SequencePoint.HiddenLine)
                        {
                            continue;
                        }

                        DebugInfo debugInfo = new DebugInfo();
                        debugInfo.lineNumber = point.StartLine;
                        debugInfo.fileName   = openedReader.Reader.GetString(openedReader.Reader.GetDocument(point.Document).Name);
                        debugInfo.ilOffset   = point.Offset;
                        points.Add(debugInfo);
                    }
                }
                catch
                {
                    return(false);
                }
            }
            return(true);
        }
Пример #10
0
        /// <summary>
        /// Helper method to return local variable name for given local index and IL offset.
        /// </summary>
        /// <param name="openedReader">symbol reader returned by LoadSymbolsForModule</param>
        /// <param name="methodToken">method token</param>
        /// <param name="localIndex">local variable index</param>
        /// <param name="localVarName">local variable name return</param>
        /// <returns>true if name has been found</returns>
        private bool GetLocalVariableByIndex(
            OpenedReader openedReader,
            int methodToken,
            int localIndex,
            out string localVarName)
        {
            localVarName = null;
            MetadataReader reader = openedReader.Reader;

            try
            {
                Handle handle = MetadataTokens.Handle(methodToken);
                if (handle.Kind != HandleKind.MethodDefinition)
                {
                    return(false);
                }

                MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
                LocalScopeHandleCollection   localScopes       = reader.GetLocalScopes(methodDebugHandle);
                foreach (LocalScopeHandle scopeHandle in localScopes)
                {
                    LocalScope scope = reader.GetLocalScope(scopeHandle);
                    LocalVariableHandleCollection localVars = scope.GetLocalVariables();
                    foreach (LocalVariableHandle varHandle in localVars)
                    {
                        LocalVariable localVar = reader.GetLocalVariable(varHandle);
                        if (localVar.Index == localIndex)
                        {
                            if (localVar.Attributes == LocalVariableAttributes.DebuggerHidden)
                            {
                                return(false);
                            }

                            localVarName = reader.GetString(localVar.Name);
                            return(true);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError($"GetLocalVariableByIndex: {ex.Message}");
            }
            return(false);
        }
Пример #11
0
        private static OpenedReader TryOpenReaderForInMemoryPdb(Stream pdbStream)
        {
            Debug.Assert(pdbStream != null);

            byte[] buffer = new byte[sizeof(uint)];
            if (pdbStream.Read(buffer, 0, sizeof(uint)) != sizeof(uint))
            {
                return(null);
            }
            uint signature = BitConverter.ToUInt32(buffer, 0);

            // quick check to avoid throwing exceptions below in common cases:
            const uint ManagedMetadataSignature = 0x424A5342;

            if (signature != ManagedMetadataSignature)
            {
                // not a Portable PDB
                return(null);
            }

            OpenedReader           result   = null;
            MetadataReaderProvider provider = null;

            try
            {
                pdbStream.Position = 0;
                provider           = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
                result             = new OpenedReader(provider, provider.GetMetadataReader());
            }
            catch (Exception e) when(e is BadImageFormatException || e is IOException)
            {
                return(null);
            }
            finally
            {
                if (result == null)
                {
                    provider?.Dispose();
                }
            }

            return(result);
        }
Пример #12
0
        /// <summary>
        /// Returns local variable name for given local index and IL offset.
        /// </summary>
        /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param>
        /// <param name="methodToken">method token</param>
        /// <param name="localIndex">local variable index</param>
        /// <param name="localVarName">local variable name return</param>
        /// <returns>true if name has been found</returns>
        private bool GetLocalVariableName(
            IntPtr self,
            IntPtr symbolReaderHandle,
            int methodToken,
            int localIndex,
            out IntPtr localVarName)
        {
            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
            localVarName = IntPtr.Zero;

            GCHandle     gch          = GCHandle.FromIntPtr(symbolReaderHandle);
            OpenedReader openedReader = (OpenedReader)gch.Target;

            if (!GetLocalVariableByIndex(openedReader, methodToken, localIndex, out string localVar))
            {
                return(false);
            }
            localVarName = Marshal.StringToBSTR(localVar);
            return(true);
        }
Пример #13
0
        /// <summary>
        /// Returns source line number and source file name for given IL offset and method token.
        /// </summary>
        /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param>
        /// <param name="methodToken">method token</param>
        /// <param name="ilOffset">IL offset</param>
        /// <param name="lineNumber">source line number return</param>
        /// <param name="fileName">source file name return</param>
        /// <returns> true if information is available</returns>
        private bool GetLineByILOffset(
            IntPtr self,
            IntPtr symbolReaderHandle,
            int methodToken,
            long ilOffset,
            out int lineNumber,
            out IntPtr fileName)
        {
            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
            fileName = IntPtr.Zero;

            GCHandle     gch          = GCHandle.FromIntPtr(symbolReaderHandle);
            OpenedReader openedReader = (OpenedReader)gch.Target;

            if (!GetSourceLineByILOffset(openedReader, methodToken, ilOffset, out lineNumber, out string sourceFileName))
            {
                return(false);
            }
            fileName = Marshal.StringToBSTR(sourceFileName);
            return(true);
        }
Пример #14
0
        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), GetFileName(pdbPath));
                    }
                    catch
                    {
                        // invalid characters in CodeView path
                        return(null);
                    }
                }

                var pdbStream = TryOpenFile(pdbPath);
                if (pdbStream == null)
                {
                    // workaround, since NI file could be generated in `.native_image` subdirectory
                    // NOTE this is temporary solution until we add option for specifying pdb path
                    try
                    {
                        int tmpLastIndex = assemblyPath.LastIndexOf(".native_image");
                        if (tmpLastIndex == -1)
                        {
                            return(null);
                        }
                        string tmpPath = assemblyPath.Substring(0, tmpLastIndex);
                        pdbPath = Path.Combine(Path.GetDirectoryName(tmpPath), GetFileName(pdbPath));
                    }
                    catch
                    {
                        // invalid characters in CodeView path
                        return(null);
                    }

                    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);
        }
        private unsafe static OpenedReader TryOpenReaderForInMemoryPdb(IntPtr inMemoryPdbAddress, int inMemoryPdbSize)
        {
            Debug.Assert(inMemoryPdbAddress != IntPtr.Zero);

            // quick check to avoid throwing exceptions below in common cases:
            const uint ManagedMetadataSignature = 0x424A5342;
            if (inMemoryPdbSize < sizeof(uint) || *(uint*)inMemoryPdbAddress != ManagedMetadataSignature)
            {
                // not a Portable PDB
                return null;
            }

            OpenedReader result = null;
            MetadataReaderProvider provider = null;
            try
            {
                provider = MetadataReaderProvider.FromMetadataImage((byte*)inMemoryPdbAddress, inMemoryPdbSize);
                result = new OpenedReader(provider, provider.GetMetadataReader());
            }
            catch (BadImageFormatException)
            {
                return null;
            }
            finally
            {
                if (result == null)
                {
                    provider?.Dispose();
                }
            }

            return result;
        }
Пример #16
0
        private static OpenedReader TryOpenReaderForInMemoryPdb(Stream pdbStream)
        {
            Debug.Assert(pdbStream != null);

            byte[] buffer = new byte[sizeof(uint)];
            if (pdbStream.Read(buffer, 0, sizeof(uint)) != sizeof(uint))
            {
                return null;
            }
            uint signature = BitConverter.ToUInt32(buffer, 0);

            // quick check to avoid throwing exceptions below in common cases:
            const uint ManagedMetadataSignature = 0x424A5342;
            if (signature != ManagedMetadataSignature)
            {
                // not a Portable PDB
                return null;
            }

            OpenedReader result = null;
            MetadataReaderProvider provider = null;
            try
            {
                pdbStream.Position = 0;
                provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
                result = new OpenedReader(provider, provider.GetMetadataReader());
            }
            catch (Exception e) when (e is BadImageFormatException || e is IOException)
            {
                return null;
            }
            finally
            {
                if (result == null)
                {
                    provider?.Dispose();
                }
            }

            return result;
        }
Пример #17
0
        private static OpenedReader TryOpenReaderFromEmbeddedPdb(PEReader peReader, DebugDirectoryEntry embeddedPdbEntry)
        {
            OpenedReader result = null;
            MetadataReaderProvider provider = null;

            try
            {
                // TODO: We might want to cache this provider globally (across stack traces), 
                // since decompressing embedded PDB takes some time.
                provider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry);
                result = new OpenedReader(provider, provider.GetMetadataReader());
            }
            catch (Exception e) when (e is BadImageFormatException || e is IOException)
            {
                return null;
            }
            finally
            {
                if (result == null)
                {
                    provider?.Dispose();
                }
            }

            return result;
        }
Пример #18
0
        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;
        }