Exemple #1
0
        private bool TryOpenCodeViewPortablePdb(DebugDirectoryEntry codeViewEntry, string peImageDirectory, Func <string, Stream?> pdbFileStreamProvider, out MetadataReaderProvider?provider, out string?pdbPath, ref Exception?errorToReport)
        {
            pdbPath  = null;
            provider = null;

            CodeViewDebugDirectoryData data;

            try
            {
                data = ReadCodeViewDebugDirectoryData(codeViewEntry);
            }
            catch (Exception e) when(e is BadImageFormatException || e is IOException)
            {
                errorToReport = errorToReport ?? e;
                return(false);
            }

            var id = new BlobContentId(data.Guid, codeViewEntry.Stamp);

            // The interpretation os the path in the CodeView needs to be platform agnostic,
            // so that PDBs built on Windows work on Unix-like systems and vice versa.
            // System.IO.Path.GetFileName() on Unix-like systems doesn't treat '\' as a file name separator,
            // so we need a custom implementation. Also avoid throwing an exception if the path contains invalid characters,
            // they might not be invalid on the other platform. It's up to the FS APIs to deal with that when opening the stream.
            string collocatedPdbPath = PathUtilities.CombinePathWithRelativePath(peImageDirectory, PathUtilities.GetFileName(data.Path));

            if (TryOpenPortablePdbFile(collocatedPdbPath, id, pdbFileStreamProvider, out provider, ref errorToReport))
            {
                pdbPath = collocatedPdbPath;
                return(true);
            }

            return(false);
        }
Exemple #2
0
        /// <summary>
        /// Reads the data pointed to by the specified Debug Directory entry and interprets them as CodeView.
        /// </summary>
        /// <exception cref="ArgumentException"><paramref name="entry"/> is not a CodeView entry.</exception>
        /// <exception cref="BadImageFormatException">Bad format of the data.</exception>
        /// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
        public unsafe CodeViewDebugDirectoryData ReadCodeViewDebugDirectoryData(DebugDirectoryEntry entry)
        {
            if (entry.Type != DebugDirectoryEntryType.CodeView)
            {
                throw new ArgumentException(SR.NotCodeViewEntry, nameof(entry));
            }

            using (var block = GetDebugDirectoryEntryDataBlock(entry))
            {
                var reader = new BlobReader(block.Pointer, block.Size);

                if (reader.ReadByte() != (byte)'R' ||
                    reader.ReadByte() != (byte)'S' ||
                    reader.ReadByte() != (byte)'D' ||
                    reader.ReadByte() != (byte)'S')
                {
                    throw new BadImageFormatException(SR.UnexpectedCodeViewDataSignature);
                }

                Guid   guid = reader.ReadGuid();
                int    age  = reader.ReadInt32();
                string path = reader.ReadUtf8NullTerminated();

                // path may be padded with NULs
                while (reader.RemainingBytes > 0)
                {
                    if (reader.ReadByte() != 0)
                    {
                        throw new BadImageFormatException(SR.InvalidPathPadding);
                    }
                }

                return(new CodeViewDebugDirectoryData(guid, age, path));
            }
        }
        partial void TryOpenEmbeddedPortablePdb(DebugDirectoryEntry embeddedPdbEntry, ref bool openedEmbeddedPdb, ref MetadataReaderProvider provider, ref Exception errorToReport)
        {
            provider = null;
            MetadataReaderProvider candidate = null;

            try
            {
                candidate = ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry);

                // throws if headers are invalid:
                candidate.GetMetadataReader();

                provider          = candidate;
                openedEmbeddedPdb = true;
                return;
            }
            catch (Exception e) when(e is BadImageFormatException || e is IOException)
            {
                errorToReport     = errorToReport ?? e;
                openedEmbeddedPdb = false;
            }
            finally
            {
                if (candidate == null)
                {
                    candidate?.Dispose();
                }
            }
        }
Exemple #4
0
        /// <summary>
        /// Reads the data pointed to by the specified Debug Directory entry and interprets them as CodeView.
        /// </summary>
        /// <exception cref="ArgumentException"><paramref name="entry"/> is not a CodeView entry.</exception>
        /// <exception cref="BadImageFormatException">Bad format of the data.</exception>
        /// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
        /// <exception cref="InvalidOperationException">PE image not available.</exception>
        public CodeViewDebugDirectoryData ReadCodeViewDebugDirectoryData(DebugDirectoryEntry entry)
        {
            if (entry.Type != DebugDirectoryEntryType.CodeView)
            {
                Throw.InvalidArgument(SR.Format(SR.UnexpectedDebugDirectoryType, nameof(DebugDirectoryEntryType.CodeView)), nameof(entry));
            }

            using (var block = GetDebugDirectoryEntryDataBlock(entry))
            {
                return(DecodeCodeViewDebugDirectoryData(block));
            }
        }
Exemple #5
0
        /// <summary>
        /// Reads the data pointed to by the specified Debug Directory entry and interprets them as PDB Checksum entry.
        /// </summary>
        /// <exception cref="ArgumentException"><paramref name="entry"/> is not a PDB Checksum entry.</exception>
        /// <exception cref="BadImageFormatException">Bad format of the data.</exception>
        /// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
        /// <exception cref="InvalidOperationException">PE image not available.</exception>
        public PdbChecksumDebugDirectoryData ReadPdbChecksumDebugDirectoryData(DebugDirectoryEntry entry)
        {
            if (entry.Type != DebugDirectoryEntryType.PdbChecksum)
            {
                Throw.InvalidArgument(SR.Format(SR.UnexpectedDebugDirectoryType, nameof(DebugDirectoryEntryType.PdbChecksum)), nameof(entry));
            }

            using (var block = GetDebugDirectoryEntryDataBlock(entry))
            {
                return(DecodePdbChecksumDebugDirectoryData(block));
            }
        }
        /// <summary>
        /// Reads the data pointed to by the specified Debug Directory entry and interprets them as Embedded Portable PDB blob.
        /// </summary>
        /// <returns>
        /// Provider of a metadata reader reading Portable PDB image.
        /// </returns>
        /// <exception cref="ArgumentException"><paramref name="entry"/> is not a <see cref="DebugDirectoryEntryType.EmbeddedPortablePdb"/> entry.</exception>
        /// <exception cref="BadImageFormatException">Bad format of the data.</exception>
        /// <exception cref="InvalidOperationException">PE image not available.</exception>
        public MetadataReaderProvider ReadEmbeddedPortablePdbDebugDirectoryData(DebugDirectoryEntry entry)
        {
            if (entry.Type != DebugDirectoryEntryType.EmbeddedPortablePdb)
            {
                Throw.InvalidArgument(SR.Format(SR.UnexpectedDebugDirectoryType, nameof(DebugDirectoryEntryType.EmbeddedPortablePdb)), nameof(entry));
            }

            ValidateEmbeddedPortablePdbVersion(entry);

            using (var block = GetDebugDirectoryEntryDataBlock(entry))
            {
                var pdbImage = DecodeEmbeddedPortablePdbDebugDirectoryData(block);
                return(MetadataReaderProvider.FromPortablePdbImage(pdbImage));
            }
        }
Exemple #7
0
        // internal for testing
        internal static void ValidateEmbeddedPortablePdbVersion(DebugDirectoryEntry entry)
        {
            // Major version encodes the version of Portable PDB format itself.
            // Minor version encodes the version of Embedded Portable PDB blob.
            // Accept any version of Portable PDB >= 1.0,
            // but only accept version 1.* of the Embedded Portable PDB blob.
            // Any breaking change in the format should rev major version of the embedded blob.
            ushort formatVersion = entry.MajorVersion;

            if (formatVersion < PortablePdbVersions.MinFormatVersion)
            {
                throw new BadImageFormatException(SR.Format(SR.UnsupportedFormatVersion, PortablePdbVersions.Format(formatVersion)));
            }

            ushort embeddedBlobVersion = entry.MinorVersion;

            if (embeddedBlobVersion != PortablePdbVersions.DefaultEmbeddedVersion)
            {
                throw new BadImageFormatException(SR.Format(SR.UnsupportedFormatVersion, PortablePdbVersions.Format(embeddedBlobVersion)));
            }
        }
        public static CodeViewDebugDirectoryData ReadCodeViewDebugDirectoryData(Stream peStream, DebugDirectoryEntry entry)
        {
            var reader = new BinaryReader(peStream);
            peStream.Position = entry.DataPointer;

            if (reader.ReadByte() != (byte)'R' ||
                reader.ReadByte() != (byte)'S' ||
                reader.ReadByte() != (byte)'D' ||
                reader.ReadByte() != (byte)'S')
            {
                throw new BadImageFormatException();
            }

            byte[] guidBlob = new byte[16];
            reader.Read(guidBlob, 0, guidBlob.Length);

            int age = reader.ReadInt32();

            byte[] pathBlob = new byte[entry.DataSize - 24];
            reader.Read(pathBlob, 0, pathBlob.Length);

            int terminator = Array.IndexOf(pathBlob, (byte)0);
            if (terminator < 0)
            {
                throw new BadImageFormatException("Path should be NUL terminated");
            }

            for (int i = terminator + 1; i < pathBlob.Length; i++)
            {
                if (pathBlob[i] != 0)
                {
                    throw new BadImageFormatException();
                }
            }

            // TODO: handle unpaired surrogates
            string path = Encoding.UTF8.GetString(pathBlob, 0, terminator);
            return new CodeViewDebugDirectoryData(path, new Guid(guidBlob), age);
        }
Exemple #9
0
        /// <summary>
        /// Reads the data pointed to by the specified Debug Directory entry and interprets them as Embedded Portable PDB blob.
        /// </summary>
        /// <returns>
        /// Provider of a metadata reader reading Portable PDB image.
        /// </returns>
        /// <exception cref="ArgumentException"><paramref name="entry"/> is not a <see cref="DebugDirectoryEntryType.EmbeddedPortablePdb"/> entry.</exception>
        /// <exception cref="BadImageFormatException">Bad format of the data.</exception>
        public unsafe MetadataReaderProvider ReadEmbeddedPortablePdbDebugDirectoryData(DebugDirectoryEntry entry)
        {
            if (entry.Type != DebugDirectoryEntryType.EmbeddedPortablePdb)
            {
                throw new ArgumentException(SR.NotCodeViewEntry, nameof(entry));
            }

            ValidateEmbeddedPortablePdbVersion(entry);

            using (AbstractMemoryBlock block = _peImage.GetMemoryBlock(entry.DataPointer, entry.DataSize))
            {
                var pdbImage = DecodeEmbeddedPortablePdbDebugDirectoryData(block);
                return MetadataReaderProvider.FromPortablePdbImage(pdbImage);
            }
        }
Exemple #10
0
        /// <summary>
        /// Reads the data pointed to by the specified Debug Directory entry and interprets them as Embedded Portable PDB blob.
        /// </summary>
        /// <returns>
        /// Provider of a metadata reader reading Portable PDB image.
        /// </returns>
        /// <exception cref="ArgumentException"><paramref name="entry"/> is not a <see cref="DebugDirectoryEntryType.EmbeddedPortablePdb"/> entry.</exception>
        /// <exception cref="BadImageFormatException">Bad format of the data.</exception>
        public unsafe MetadataReaderProvider ReadEmbeddedPortablePdbDebugDirectoryData(DebugDirectoryEntry entry)
        {
            if (entry.Type != DebugDirectoryEntryType.EmbeddedPortablePdb)
            {
                throw new ArgumentException(SR.NotCodeViewEntry, nameof(entry));
            }

            ValidateEmbeddedPortablePdbVersion(entry);

            using (var block = GetDebugDirectoryEntryDataBlock(entry))
            {
                var pdbImage = DecodeEmbeddedPortablePdbDebugDirectoryData(block);
                return(MetadataReaderProvider.FromPortablePdbImage(pdbImage));
            }
        }
Exemple #11
0
 partial void TryOpenEmbeddedPortablePdb(DebugDirectoryEntry embeddedPdbEntry, ref bool openedEmbeddedPdb, ref MetadataReaderProvider?provider, ref Exception?errorToReport);
Exemple #12
0
        private AbstractMemoryBlock GetDebugDirectoryEntryDataBlock(DebugDirectoryEntry entry)
        {
            int dataOffset = IsLoadedImage ? entry.DataRelativeVirtualAddress : entry.DataPointer;

            return(_peImage.GetMemoryBlock(dataOffset, entry.DataSize));
        }
Exemple #13
0
        /// <summary>
        /// Reads the data pointed to by the specified Debug Directory entry and interprets them as Embedded Portable PDB blob.
        /// </summary>
        /// <returns>
        /// Provider of a metadata reader reading the embedded Portable PDB image.
        /// Dispose to release resources allocated for the embedded PDB.
        /// </returns>
        /// <exception cref="ArgumentException"><paramref name="entry"/> is not a <see cref="DebugDirectoryEntryType.EmbeddedPortablePdb"/> entry.</exception>
        /// <exception cref="BadImageFormatException">Bad format of the data.</exception>
        /// <exception cref="InvalidOperationException">PE image not available.</exception>
        public unsafe MetadataReaderProvider ReadEmbeddedPortablePdbDebugDirectoryData(DebugDirectoryEntry entry)
        {
            if (entry.Type != DebugDirectoryEntryType.EmbeddedPortablePdb)
            {
                Throw.InvalidArgument(SR.Format(SR.UnexpectedDebugDirectoryType, nameof(DebugDirectoryEntryType.EmbeddedPortablePdb)), nameof(entry));
            }

            ValidateEmbeddedPortablePdbVersion(entry);

            using var block = GetDebugDirectoryEntryDataBlock(entry);
            return(new MetadataReaderProvider(DecodeEmbeddedPortablePdbDebugDirectoryData(block)));
        }
Exemple #14
0
        // internal for testing
        internal static void ValidateEmbeddedPortablePdbVersion(DebugDirectoryEntry entry)
        {
            // Major version encodes the version of Portable PDB format itself.
            // Minor version encodes the version of Embedded Portable PDB blob.
            // Accept any version of Portable PDB >= 1.0, 
            // but only accept version 1.* of the Embedded Portable PDB blob.
            // Any breaking change in the format should rev major version of the embedded blob.
            ushort formatVersion = entry.MajorVersion;
            if (formatVersion < PortablePdbVersions.MinFormatVersion)
            {
                throw new BadImageFormatException(SR.Format(SR.UnsupportedFormatVersion, PortablePdbVersions.Format(formatVersion)));
            }

            ushort embeddedBlobVersion = entry.MinorVersion;
            if (embeddedBlobVersion != PortablePdbVersions.DefaultEmbeddedVersion)
            {
                throw new BadImageFormatException(SR.Format(SR.UnsupportedFormatVersion, PortablePdbVersions.Format(embeddedBlobVersion)));
            }
        }
Exemple #15
0
        /// <summary>
        /// Reads the data pointed to by the specified Debug Directory entry and interprets them as CodeView.
        /// </summary>
        /// <exception cref="ArgumentException"><paramref name="entry"/> is not a CodeView entry.</exception>
        /// <exception cref="BadImageFormatException">Bad format of the data.</exception>
        /// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
        public unsafe CodeViewDebugDirectoryData ReadCodeViewDebugDirectoryData(DebugDirectoryEntry entry)
        {
            if (entry.Type != DebugDirectoryEntryType.CodeView)
            {
                throw new ArgumentException(SR.NotCodeViewEntry, nameof(entry));
            }

            using (AbstractMemoryBlock block = _peImage.GetMemoryBlock(entry.DataPointer, entry.DataSize))
            {
                var reader = new BlobReader(block.Pointer, block.Size);

                if (reader.ReadByte() != (byte)'R' ||
                    reader.ReadByte() != (byte)'S' ||
                    reader.ReadByte() != (byte)'D' ||
                    reader.ReadByte() != (byte)'S')
                {
                    throw new BadImageFormatException(SR.UnexpectedCodeViewDataSignature);
                }

                Guid guid = reader.ReadGuid();
                int age = reader.ReadInt32();
                string path = reader.ReadUtf8NullTerminated();

                // path may be padded with NULs
                while (reader.RemainingBytes > 0)
                {
                    if (reader.ReadByte() != 0)
                    {
                        throw new BadImageFormatException(SR.InvalidPathPadding);
                    }
                }

                return new CodeViewDebugDirectoryData(guid, age, path);
            }
        }
Exemple #16
0
        public static CodeViewDebugDirectoryData ReadCodeViewDebugDirectoryData(Stream peStream, DebugDirectoryEntry entry)
        {
            var reader = new BinaryReader(peStream);

            peStream.Position = entry.DataPointer;

            if (reader.ReadByte() != (byte)'R' ||
                reader.ReadByte() != (byte)'S' ||
                reader.ReadByte() != (byte)'D' ||
                reader.ReadByte() != (byte)'S')
            {
                throw new BadImageFormatException();
            }

            byte[] guidBlob = new byte[16];
            reader.Read(guidBlob, 0, guidBlob.Length);

            int age = reader.ReadInt32();

            byte[] pathBlob = new byte[entry.DataSize - 24];
            reader.Read(pathBlob, 0, pathBlob.Length);

            int terminator = Array.IndexOf(pathBlob, (byte)0);

            if (terminator < 0)
            {
                throw new BadImageFormatException("Path should be NUL terminated");
            }

            for (int i = terminator + 1; i < pathBlob.Length; i++)
            {
                if (pathBlob[i] != 0)
                {
                    throw new BadImageFormatException();
                }
            }

            // TODO: handle unpaired surrogates
            string path = Encoding.UTF8.GetString(pathBlob, 0, terminator);

            return(new CodeViewDebugDirectoryData(path, new Guid(guidBlob), age));
        }
Exemple #17
0
        /// <summary>
        /// Reads the data pointed to by the specified Debug Directory entry and interprets them as PDB Checksum entry.
        /// </summary>
        /// <exception cref="ArgumentException"><paramref name="entry"/> is not a PDB Checksum entry.</exception>
        /// <exception cref="BadImageFormatException">Bad format of the data.</exception>
        /// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
        /// <exception cref="InvalidOperationException">PE image not available.</exception>
        internal static PdbChecksumDebugDirectoryData ReadPdbChecksumDebugDirectoryData(this PEReader peReader, DebugDirectoryEntry entry)
        {
            if (entry.Type != PdbChecksumEntryType)
            {
                throw new ArgumentException("Unexpected debug directory entry type", nameof(entry));
            }

            var peImage = peReader.GetEntireImage();

            int dataOffset = peReader.IsLoadedImage ? entry.DataRelativeVirtualAddress : entry.DataPointer;
            var reader     = peImage.GetReader(dataOffset, entry.DataSize);

            int nameLength = reader.IndexOf(0);

            if (nameLength <= 0)
            {
                throw new BadImageFormatException("Invalid PDB Checksum data format");
            }

            string algorithmName = reader.ReadUTF8(nameLength);

            // NUL
            reader.Offset += 1;

            var checksum = reader.ReadBytes(reader.RemainingBytes).ToImmutableArray();

            if (checksum.Length == 0)
            {
                throw new BadImageFormatException("Invalid PDB Checksum data format");
            }

            return(new PdbChecksumDebugDirectoryData(algorithmName, checksum));
        }
Exemple #18
0
 private AbstractMemoryBlock GetDebugDirectoryEntryDataBlock(DebugDirectoryEntry entry)
 {
     int dataOffset = IsLoadedImage ? entry.DataRelativeVirtualAddress : entry.DataPointer;
     return _peImage.GetMemoryBlock(dataOffset, entry.DataSize);
 }
Exemple #19
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;
        }
Exemple #20
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;
        }
Exemple #21
0
        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;
                }
            }
        }