/// <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> /// 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(); } }