示例#1
0
        private static string DecodeUtf8Prefixed(byte* bytes, int byteCount, byte[] prefix, MetadataStringDecoder utf8Decoder)
        {
            Debug.Assert(utf8Decoder != null);

            int prefixedByteCount = byteCount + prefix.Length;

            if (prefixedByteCount == 0)
            {
                return String.Empty;
            }

            byte[] buffer = AcquireBuffer(prefixedByteCount);

            prefix.CopyTo(buffer, 0);
            Marshal.Copy((IntPtr)bytes, buffer, prefix.Length, byteCount);

            string result;
            fixed (byte* prefixedBytes = buffer)
            {
                result = utf8Decoder.GetString(prefixedBytes, prefixedByteCount);
            }

            ReleaseBuffer(buffer);
            return result;
        }
        public static PdbSymbolReader TryOpen(string pdbFilename, MetadataStringDecoder stringDecoder)
        {
            MemoryMappedViewAccessor mappedViewAccessor;
            MetadataReader reader = TryOpenMetadataFile(pdbFilename, stringDecoder, out mappedViewAccessor);
            if (reader == null)
                return null;

            return new PortablePdbSymbolReader(reader, mappedViewAccessor);
        }
示例#3
0
        /// <summary>
        /// Gets a <see cref="MetadataReader"/> from a <see cref="PEReader"/>.
        /// </summary>
        /// <remarks>
        /// The caller must keep the <see cref="PEReader"/> alive and undisposed throughout the lifetime of the metadata reader.
        /// </remarks>
        /// <exception cref="ArgumentNullException"><paramref name="peReader"/> is null</exception>
        /// <exception cref="ArgumentException">The encoding of <paramref name="utf8Decoder"/> is not <see cref="UTF8Encoding"/>.</exception>
        /// <exception cref="PlatformNotSupportedException">The current platform is big-endian.</exception>
        /// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
        public static unsafe MetadataReader GetMetadataReader(this PEReader peReader, MetadataReaderOptions options, MetadataStringDecoder utf8Decoder)
        {
            if (peReader == null)
            {
                throw new ArgumentNullException(nameof(peReader));
            }

            var metadata = peReader.GetMetadata();
            return new MetadataReader(metadata.Pointer, metadata.Length, options, utf8Decoder);
        }
示例#4
0
        public static string DecodeUtf8(byte* bytes, int byteCount, byte[] prefix, MetadataStringDecoder utf8Decoder)
        {
            Debug.Assert(utf8Decoder != null);

            if (prefix != null)
            {
                return DecodeUtf8Prefixed(bytes, byteCount, prefix, utf8Decoder);
            }

            if (byteCount == 0)
            {
                return String.Empty;
            }

            return utf8Decoder.GetString(bytes, byteCount);
        }
        private static unsafe MetadataReader TryOpenMetadataFile(string filePath, MetadataStringDecoder stringDecoder, out MemoryMappedViewAccessor mappedViewAccessor)
        {
            FileStream fileStream = null;
            MemoryMappedFile mappedFile = null;
            MemoryMappedViewAccessor accessor = null;
            try
            {
                // Create stream because CreateFromFile(string, ...) uses FileShare.None which is too strict
                fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);
                mappedFile = MemoryMappedFile.CreateFromFile(
                    fileStream, null, fileStream.Length, MemoryMappedFileAccess.Read, HandleInheritability.None, true);

                accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);

                var safeBuffer = accessor.SafeMemoryMappedViewHandle;

                // Check whether this is a real metadata file to avoid thrown and caught exceptions
                // for non-portable .pdbs
                if (safeBuffer.Read<byte>(0) != 'B' || // COR20MetadataSignature
                    safeBuffer.Read<byte>(1) != 'S' ||
                    safeBuffer.Read<byte>(2) != 'J' ||
                    safeBuffer.Read<byte>(3) != 'B')
                {
                    mappedViewAccessor = null;
                    return null;
                }

                var metadataReader = new MetadataReader((byte*)safeBuffer.DangerousGetHandle(), (int)safeBuffer.ByteLength, MetadataReaderOptions.Default, stringDecoder);

                // MemoryMappedFile does not need to be kept around. MemoryMappedViewAccessor is enough.

                mappedViewAccessor = accessor;
                accessor = null;

                return metadataReader;
            }
            finally
            {
                if (accessor != null)
                    accessor.Dispose();
                if (mappedFile != null)
                    mappedFile.Dispose();
                if (fileStream != null)
                    fileStream.Dispose();
            }
        }
示例#6
0
        /// <summary>
        /// Gets a <see cref="MetadataReader"/> from a <see cref="PEReader"/>.
        /// </summary>
        /// <remarks>
        /// The caller must keep the <see cref="PEReader"/> alive and undisposed throughout the lifetime of the metadata reader.
        /// </remarks>
        /// <exception cref="ArgumentNullException"><paramref name="peReader"/> is null</exception>
        /// <exception cref="ArgumentException">The encoding of <paramref name="utf8Decoder"/> is not <see cref="UTF8Encoding"/>.</exception>
        /// <exception cref="PlatformNotSupportedException">The current platform is big-endian.</exception>
        /// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
        public static unsafe MetadataReader GetMetadataReader(this PEReader peReader, MetadataReaderOptions options, MetadataStringDecoder utf8Decoder)
        {
            if (peReader == null)
            {
                throw new ArgumentNullException(nameof(peReader));
            }

            var metadata = peReader.GetMetadata();

            return(new MetadataReader(metadata.Pointer, metadata.Length, options, utf8Decoder));
        }
示例#7
0
        // comparison stops at null terminator, terminator parameter, or end-of-block -- whichever comes first.
        internal bool Utf8NullTerminatedStartsWith(int offset, string text, MetadataStringDecoder utf8Decoder, char terminator = '\0')
        {
            FastComparisonResult result = Utf8NullTerminatedFastCompare(offset, text, terminator);

            switch (result)
            {
                case FastComparisonResult.Equal:
                case FastComparisonResult.IsPrefix:
                    return true;

                case FastComparisonResult.Unequal:
                    return false;

                default:
                    Debug.Assert(result == FastComparisonResult.Inconclusive);
                    int bytesRead;
                    string decoded = PeekUtf8NullTerminated(offset, null, utf8Decoder, out bytesRead, terminator);
                    return decoded.StartsWith(text, StringComparison.Ordinal);
            }
        }
示例#8
0
 /// <summary>
 /// Gets a <see cref="MetadataReader"/> from a <see cref="MetadataReaderProvider"/>.
 /// </summary>
 /// <remarks>
 /// The caller must keep the <see cref="MetadataReaderProvider"/> alive and undisposed throughout the lifetime of the metadata reader.
 /// </remarks>
 /// <exception cref="ArgumentException">The encoding of <paramref name="utf8Decoder"/> is not <see cref="UTF8Encoding"/>.</exception>
 /// <exception cref="PlatformNotSupportedException">The current platform is big-endian.</exception>
 /// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
 public unsafe MetadataReader GetMetadataReader(MetadataReaderOptions options = MetadataReaderOptions.Default, MetadataStringDecoder utf8Decoder = null)
 {
     AbstractMemoryBlock metadata = GetMetadataBlock();
     return new MetadataReader(metadata.Pointer, metadata.Size, options, utf8Decoder);
 }
示例#9
0
        /// <summary>
        /// Creates a metadata reader from the metadata stored at the given memory location.
        /// </summary>
        /// <remarks>
        /// The memory is owned by the caller and it must be kept memory alive and unmodified throughout the lifetime of the <see cref="MetadataReader"/>.
        /// Use <see cref="PEReaderExtensions.GetMetadataReader(PortableExecutable.PEReader, MetadataReaderOptions, MetadataStringDecoder)"/> to obtain 
        /// metadata from a PE image.
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="length"/> is not positive.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="metadata"/> is null.</exception>
        /// <exception cref="ArgumentException">The encoding of <paramref name="utf8Decoder"/> is not <see cref="UTF8Encoding"/>.</exception>
        /// <exception cref="PlatformNotSupportedException">The current platform is big-endian.</exception>
        public unsafe MetadataReader(byte* metadata, int length, MetadataReaderOptions options, MetadataStringDecoder utf8Decoder)
        {
            // Do not throw here when length is 0. We'll throw BadImageFormatException later on, so that the caller doesn't need to 
            // worry about the image (stream) being empty and can handle all image errors by catching BadImageFormatException.
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (metadata == null)
            {
                throw new ArgumentNullException(nameof(metadata));
            }

            if (utf8Decoder == null)
            {
                utf8Decoder = MetadataStringDecoder.DefaultUTF8;
            }

            if (!(utf8Decoder.Encoding is UTF8Encoding))
            {
                throw new ArgumentException(SR.MetadataStringDecoderEncodingMustBeUtf8, nameof(utf8Decoder));
            }

            if (!BitConverter.IsLittleEndian)
            {
                Throw.LitteEndianArchitectureRequired();
            }

            this.Block = new MemoryBlock(metadata, length);

            _options = options;
            this.UTF8Decoder = utf8Decoder;

            var headerReader = new BlobReader(this.Block);
            this.ReadMetadataHeader(ref headerReader, out _versionString);
            _metadataKind = GetMetadataKind(_versionString);
            var streamHeaders = this.ReadStreamHeaders(ref headerReader);

            // storage header and stream headers:
            MemoryBlock metadataTableStream;
            MemoryBlock standalonePdbStream;
            this.InitializeStreamReaders(ref this.Block, streamHeaders, out _metadataStreamKind, out metadataTableStream, out standalonePdbStream);

            int[] externalTableRowCountsOpt;
            if (standalonePdbStream.Length > 0)
            {
                ReadStandalonePortablePdbStream(standalonePdbStream, out _debugMetadataHeader, out externalTableRowCountsOpt);
            }
            else
            {
                externalTableRowCountsOpt = null;
            }

            var tableReader = new BlobReader(metadataTableStream);

            HeapSizes heapSizes;
            int[] metadataTableRowCounts;
            this.ReadMetadataTableHeader(ref tableReader, out heapSizes, out metadataTableRowCounts, out _sortedTables);

            this.InitializeTableReaders(tableReader.GetMemoryBlockAt(0, tableReader.RemainingBytes), heapSizes, metadataTableRowCounts, externalTableRowCountsOpt);

            // This previously could occur in obfuscated assemblies but a check was added to prevent 
            // it getting to this point
            Debug.Assert(this.AssemblyTable.NumberOfRows <= 1);

            // Although the specification states that the module table will have exactly one row,
            // the native metadata reader would successfully read files containing more than one row.
            // Such files exist in the wild and may be produced by obfuscators.
            if (standalonePdbStream.Length == 0 && this.ModuleTable.NumberOfRows < 1)
            {
                throw new BadImageFormatException(SR.Format(SR.ModuleTableInvalidNumberOfRows, this.ModuleTable.NumberOfRows));
            }

            //  read 
            this.NamespaceCache = new NamespaceCache(this);

            if (_metadataKind != MetadataKind.Ecma335)
            {
                this.WinMDMscorlibRef = FindMscorlibAssemblyRefNoProjection();
            }
        }
示例#10
0
 private static bool CanReuseReader(MetadataReader reader, MetadataReaderOptions options, MetadataStringDecoder utf8DecoderOpt)
 {
     return(reader != null && reader.Options == options && ReferenceEquals(reader.UTF8Decoder, utf8DecoderOpt ?? MetadataStringDecoder.DefaultUTF8));
 }
 private static bool CanReuseReader(MetadataReader reader, MetadataReaderOptions options, MetadataStringDecoder utf8DecoderOpt)
 {
     return reader != null && reader.Options == options && ReferenceEquals(reader.Utf8Decoder, utf8DecoderOpt ?? MetadataStringDecoder.DefaultUTF8);
 }
示例#12
0
        // comparison stops at null terminator, terminator parameter, or end-of-block -- whichever comes first.
        internal bool Utf8NullTerminatedStartsWith(int offset, string text, MetadataStringDecoder utf8Decoder, char terminator, bool ignoreCase)
        {
            int endIndex;
            FastComparisonResult result = Utf8NullTerminatedFastCompare(offset, text, 0, out endIndex, terminator, ignoreCase);

            switch (result)
            {
                case FastComparisonResult.Equal:
                case FastComparisonResult.BytesStartWithText:
                    return true;

                case FastComparisonResult.Unequal:
                case FastComparisonResult.TextStartsWithBytes:
                    return false;

                default:
                    Debug.Assert(result == FastComparisonResult.Inconclusive);
                    int bytesRead;
                    string decoded = PeekUtf8NullTerminated(offset, null, utf8Decoder, out bytesRead, terminator);
                    return decoded.StartsWith(text, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
            }
        }
示例#13
0
 /// <summary>
 /// Read UTF8 at the given offset up to the given terminator, null terminator, or end-of-block.
 /// </summary>
 /// <param name="offset">Offset in to the block where the UTF8 bytes start.</param>
 /// <param name="prefix">UTF8 encoded prefix to prepend to the bytes at the offset before decoding.</param>
 /// <param name="utf8Decoder">The UTF8 decoder to use that allows user to adjust fallback and/or reuse existing strings without allocating a new one.</param>
 /// <param name="numberOfBytesRead">The number of bytes read, which includes the terminator if we did not hit the end of the block.</param>
 /// <param name="terminator">A character in the ASCII range that marks the end of the string. 
 /// If a value other than '\0' is passed we still stop at the null terminator if encountered first.</param>
 /// <returns>The decoded string.</returns>
 internal string PeekUtf8NullTerminated(int offset, byte[] prefix, MetadataStringDecoder utf8Decoder, out int numberOfBytesRead, char terminator = '\0')
 {
     Debug.Assert(terminator <= 0x7F);
     CheckBounds(offset, 0);
     int length = GetUtf8NullTerminatedLength(offset, out numberOfBytesRead, terminator);
     return EncodingHelper.DecodeUtf8(Pointer + offset, length, prefix, utf8Decoder);
 }
示例#14
0
        /// <summary>
        /// Gets a <see cref="MetadataReader"/> from a <see cref="PEReader"/>.
        /// </summary>
        /// <remarks>
        /// The caller must keep the <see cref="PEReader"/> alive and undisposed throughout the lifetime of the metadata reader.
        /// </remarks>
        public static unsafe MetadataReader GetMetadataReader(this PEReader peReader, MetadataReaderOptions options, MetadataStringDecoder utf8Decoder)
        {
            var metadata = peReader.GetMetadata();

            return(new MetadataReader(metadata.Pointer, metadata.Length, options, utf8Decoder));
        }
示例#15
0
        /// <summary>
        /// Gets a <see cref="MetadataReader"/> from a <see cref="MetadataReaderProvider"/>.
        /// </summary>
        /// <remarks>
        /// The caller must keep the <see cref="MetadataReaderProvider"/> alive and undisposed throughout the lifetime of the metadata reader.
        /// </remarks>
        /// <exception cref="ArgumentException">The encoding of <paramref name="utf8Decoder"/> is not <see cref="UTF8Encoding"/>.</exception>
        /// <exception cref="PlatformNotSupportedException">The current platform is big-endian.</exception>
        /// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
        public unsafe MetadataReader GetMetadataReader(MetadataReaderOptions options = MetadataReaderOptions.Default, MetadataStringDecoder utf8Decoder = null)
        {
            AbstractMemoryBlock metadata = GetMetadataBlock();

            return(new MetadataReader(metadata.Pointer, metadata.Size, options, utf8Decoder));
        }
 /// <summary>
 /// Gets a <see cref="MetadataReader"/> from a <see cref="PEReader"/>.
 /// </summary>
 /// <remarks>
 /// The caller must keep the <see cref="PEReader"/> alive and undisposed throughout the lifetime of the metadata reader.
 /// </remarks>
 public static unsafe MetadataReader GetMetadataReader(this PEReader peReader, MetadataReaderOptions options, MetadataStringDecoder utf8Decoder)
 {
     var metadata = peReader.GetMetadata();
     return new MetadataReader(metadata.Pointer, metadata.Length, options, utf8Decoder);
 }
 MetadataStringDecoder IMetadataStringDecoderProvider.GetMetadataStringDecoder()
 {
     if (_metadataStringDecoder == null)
         _metadataStringDecoder = new CachingMetadataStringDecoder(0x10000); // TODO: Tune the size
     return _metadataStringDecoder;
 }
示例#18
0
        // comparison stops at null terminator, terminator parameter, or end-of-block -- whichever comes first.
        internal bool Utf8NullTerminatedEquals(int offset, string text, MetadataStringDecoder utf8Decoder, char terminator, bool ignoreCase)
        {
            int firstDifference;
            FastComparisonResult result = Utf8NullTerminatedFastCompare(offset, text, 0, out firstDifference, terminator, ignoreCase);

            if (result == FastComparisonResult.Inconclusive)
            {
                int bytesRead;
                string decoded = PeekUtf8NullTerminated(offset, null, utf8Decoder, out bytesRead, terminator);
                return decoded.Equals(text, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
            }

            return result == FastComparisonResult.Equal;
        }
示例#19
0
        /// <summary>
        /// Gets a <see cref="MetadataReader"/> from a <see cref="MetadataReaderProvider"/>.
        /// </summary>
        /// <remarks>
        /// The caller must keep the <see cref="MetadataReaderProvider"/> undisposed throughout the lifetime of the metadata reader.
        ///
        /// If this method is called multiple times each call with arguments equal to the arguments passed to the previous successful call
        /// returns the same instance of <see cref="MetadataReader"/> as the previous call.
        /// </remarks>
        /// <exception cref="ArgumentException">The encoding of <paramref name="utf8Decoder"/> is not <see cref="UTF8Encoding"/>.</exception>
        /// <exception cref="PlatformNotSupportedException">The current platform is big-endian.</exception>
        /// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
        /// <exception cref="ObjectDisposedException">Provider has been disposed.</exception>
        public unsafe MetadataReader GetMetadataReader(MetadataReaderOptions options = MetadataReaderOptions.Default, MetadataStringDecoder utf8Decoder = null)
        {
            var cachedReader = _lazyMetadataReader;

            if (CanReuseReader(cachedReader, options, utf8Decoder))
            {
                return(cachedReader);
            }

            // If multiple threads attempt to open a metadata reader with the same options and decoder
            // it's cheaper to wait for the other thread to finish initializing the reader than to open
            // two readers and discard one.
            // Note that it's rare to reader the same metadata using different options.
            lock (_metadataReaderGuard)
            {
                cachedReader = _lazyMetadataReader;

                if (CanReuseReader(cachedReader, options, utf8Decoder))
                {
                    return(cachedReader);
                }

                AbstractMemoryBlock metadata = GetMetadataBlock();
                var newReader = new MetadataReader(metadata.Pointer, metadata.Size, options, utf8Decoder, memoryOwner: this);
                _lazyMetadataReader = newReader;
                return(newReader);
            }
        }
示例#20
0
        /// <summary>
        /// Creates a metadata reader from the metadata stored at the given memory location.
        /// </summary>
        /// <remarks>
        /// The memory is owned by the caller and it must be kept memory alive and unmodified throughout the lifetime of the <see cref="MetadataReader"/>.
        /// Use <see cref="PEReaderExtensions.GetMetadataReader(PortableExecutable.PEReader, MetadataReaderOptions, MetadataStringDecoder)"/> to obtain 
        /// metadata from a PE image.
        /// </remarks>
        public unsafe MetadataReader(byte* metadata, int length, MetadataReaderOptions options, MetadataStringDecoder utf8Decoder)
        {
            if (length <= 0)
            {
                throw new ArgumentOutOfRangeException("length");
            }

            if (metadata == null)
            {
                throw new ArgumentNullException("metadata");
            }

            if (utf8Decoder == null)
            {
                utf8Decoder = MetadataStringDecoder.DefaultUTF8;
            }

            if (!(utf8Decoder.Encoding is UTF8Encoding))
            {
                throw new ArgumentException(MetadataResources.MetadataStringDecoderEncodingMustBeUtf8, "utf8Decoder");
            }

            if (!BitConverter.IsLittleEndian)
            {
                throw new PlatformNotSupportedException(MetadataResources.LitteEndianArchitectureRequired);
            }

            this.Block = new MemoryBlock(metadata, length);

            this.options = options;
            this.utf8Decoder = utf8Decoder;

            BlobReader memReader = new BlobReader(this.Block);

            this.ReadMetadataHeader(ref memReader);

            // storage header and stream headers:
            MemoryBlock metadataTableStream;
            var streamHeaders = this.ReadStreamHeaders(ref memReader);
            this.InitializeStreamReaders(ref this.Block, streamHeaders, out metadataTableStream);

            memReader = new BlobReader(metadataTableStream);

            uint[] metadataTableRowCounts;
            this.ReadMetadataTableHeader(ref memReader, out metadataTableRowCounts);
            this.InitializeTableReaders(memReader.GetMemoryBlockAt(0, memReader.RemainingBytes), metadataTableRowCounts);

            // This previously could occur in obfuscated assemblies but a check was added to prevent
            // it getting to this point
            Debug.Assert(this.AssemblyTable.NumberOfRows <= 1);

            // Although the specification states that the module table will have exactly one row,
            // the native metadata reader would successfully read files containing more than one row.
            // Such files exist in the wild and may be produced by obfuscators.
            if (this.ModuleTable.NumberOfRows < 1)
            {
                throw new BadImageFormatException(string.Format(MetadataResources.ModuleTableInvalidNumberOfRows, this.ModuleTable.NumberOfRows));
            }

            //  read
            this.namespaceCache = new NamespaceCache(this);

            if (this.metadataKind != MetadataKind.Ecma335)
            {
                this.WinMDMscorlibRef = FindMscorlibAssemblyRefNoProjection();
            }
        }
        /// <summary>
        /// Gets a <see cref="MetadataReader"/> from a <see cref="MetadataReaderProvider"/>.
        /// </summary>
        /// <remarks>
        /// The caller must keep the <see cref="MetadataReaderProvider"/> alive and undisposed throughout the lifetime of the metadata reader.
        /// 
        /// If this method is called multiple times each call with arguments equal to the arguments passed to the previous successful call 
        /// returns the same instance of <see cref="MetadataReader"/> as the previous call.
        /// </remarks>
        /// <exception cref="ArgumentException">The encoding of <paramref name="utf8Decoder"/> is not <see cref="UTF8Encoding"/>.</exception>
        /// <exception cref="PlatformNotSupportedException">The current platform is big-endian.</exception>
        /// <exception cref="IOException">IO error while reading from the underlying stream.</exception>
        /// <exception cref="ObjectDisposedException">Provider has been disposed.</exception>
        public unsafe MetadataReader GetMetadataReader(MetadataReaderOptions options = MetadataReaderOptions.Default, MetadataStringDecoder utf8Decoder = null)
        {
            var cachedReader = _lazyMetadataReader;

            if (CanReuseReader(cachedReader, options, utf8Decoder))
            {
                return cachedReader;
            }

            // If multiple threads attempt to open a metadata reader with the same options and decoder 
            // it's cheaper to wait for the other thread to finish initializing the reader than to open 
            // two readers and discard one.
            // Note that it's rare to reader the same metadata using different options.
            lock (_metadataReaderGuard)
            {
                cachedReader = _lazyMetadataReader;

                if (CanReuseReader(cachedReader, options, utf8Decoder))
                {
                    return cachedReader;
                }

                AbstractMemoryBlock metadata = GetMetadataBlock();
                var newReader = new MetadataReader(metadata.Pointer, metadata.Size, options, utf8Decoder);
                _lazyMetadataReader = newReader;
                return newReader;
            }
        }