// dnlib: See LICENSE.txt for more info
using System;
using dnlib.IO;
namespace dnlib.DotNet.MD {
///
/// .NET metadata tables stream
///
public sealed partial class TablesStream : DotNetStream {
bool initialized;
uint reserved1;
byte majorVersion;
byte minorVersion;
MDStreamFlags flags;
byte log2Rid;
ulong validMask;
ulong sortedMask;
uint extraData;
MDTable[] mdTables;
uint mdTablesPos;
IColumnReader columnReader;
IRowReader methodRowReader;
readonly CLRRuntimeReaderKind runtime;
#pragma warning disable 1591 // XML doc comment
public MDTable ModuleTable { get; private set; }
public MDTable TypeRefTable { get; private set; }
public MDTable TypeDefTable { get; private set; }
public MDTable FieldPtrTable { get; private set; }
public MDTable FieldTable { get; private set; }
public MDTable MethodPtrTable { get; private set; }
public MDTable MethodTable { get; private set; }
public MDTable ParamPtrTable { get; private set; }
public MDTable ParamTable { get; private set; }
public MDTable InterfaceImplTable { get; private set; }
public MDTable MemberRefTable { get; private set; }
public MDTable ConstantTable { get; private set; }
public MDTable CustomAttributeTable { get; private set; }
public MDTable FieldMarshalTable { get; private set; }
public MDTable DeclSecurityTable { get; private set; }
public MDTable ClassLayoutTable { get; private set; }
public MDTable FieldLayoutTable { get; private set; }
public MDTable StandAloneSigTable { get; private set; }
public MDTable EventMapTable { get; private set; }
public MDTable EventPtrTable { get; private set; }
public MDTable EventTable { get; private set; }
public MDTable PropertyMapTable { get; private set; }
public MDTable PropertyPtrTable { get; private set; }
public MDTable PropertyTable { get; private set; }
public MDTable MethodSemanticsTable { get; private set; }
public MDTable MethodImplTable { get; private set; }
public MDTable ModuleRefTable { get; private set; }
public MDTable TypeSpecTable { get; private set; }
public MDTable ImplMapTable { get; private set; }
public MDTable FieldRVATable { get; private set; }
public MDTable ENCLogTable { get; private set; }
public MDTable ENCMapTable { get; private set; }
public MDTable AssemblyTable { get; private set; }
public MDTable AssemblyProcessorTable { get; private set; }
public MDTable AssemblyOSTable { get; private set; }
public MDTable AssemblyRefTable { get; private set; }
public MDTable AssemblyRefProcessorTable { get; private set; }
public MDTable AssemblyRefOSTable { get; private set; }
public MDTable FileTable { get; private set; }
public MDTable ExportedTypeTable { get; private set; }
public MDTable ManifestResourceTable { get; private set; }
public MDTable NestedClassTable { get; private set; }
public MDTable GenericParamTable { get; private set; }
public MDTable MethodSpecTable { get; private set; }
public MDTable GenericParamConstraintTable { get; private set; }
public MDTable DocumentTable { get; private set; }
public MDTable MethodDebugInformationTable { get; private set; }
public MDTable LocalScopeTable { get; private set; }
public MDTable LocalVariableTable { get; private set; }
public MDTable LocalConstantTable { get; private set; }
public MDTable ImportScopeTable { get; private set; }
public MDTable StateMachineMethodTable { get; private set; }
public MDTable CustomDebugInformationTable { get; private set; }
#pragma warning restore
///
/// Gets/sets the column reader
///
public IColumnReader ColumnReader {
get => columnReader;
set => columnReader = value;
}
///
/// Gets/sets the Method table reader
///
public IRowReader MethodRowReader {
get => methodRowReader;
set => methodRowReader = value;
}
///
/// Gets the reserved field
///
public uint Reserved1 => reserved1;
///
/// Gets the version. The major version is in the upper 8 bits, and the minor version
/// is in the lower 8 bits.
///
public ushort Version => (ushort)((majorVersion << 8) | minorVersion);
///
/// Gets
///
public MDStreamFlags Flags => flags;
///
/// Gets the reserved log2 rid field
///
public byte Log2Rid => log2Rid;
///
/// Gets the valid mask
///
public ulong ValidMask => validMask;
///
/// Gets the sorted mask
///
public ulong SortedMask => sortedMask;
///
/// Gets the extra data
///
public uint ExtraData => extraData;
///
/// Gets the MD tables
///
public MDTable[] MDTables => mdTables;
///
/// Gets the bit
///
public bool HasBigStrings => (flags & MDStreamFlags.BigStrings) != 0;
///
/// Gets the bit
///
public bool HasBigGUID => (flags & MDStreamFlags.BigGUID) != 0;
///
/// Gets the bit
///
public bool HasBigBlob => (flags & MDStreamFlags.BigBlob) != 0;
///
/// Gets the bit
///
public bool HasPadding => runtime == CLRRuntimeReaderKind.CLR && (flags & MDStreamFlags.Padding) != 0;
///
/// Gets the bit
///
public bool HasDeltaOnly => runtime == CLRRuntimeReaderKind.CLR && (flags & MDStreamFlags.DeltaOnly) != 0;
///
/// Gets the bit
///
public bool HasExtraData => runtime == CLRRuntimeReaderKind.CLR && (flags & MDStreamFlags.ExtraData) != 0;
///
/// Gets the bit
///
public bool HasDelete => runtime == CLRRuntimeReaderKind.CLR && (flags & MDStreamFlags.HasDelete) != 0;
///
/// Constructor
///
/// factory
/// Offset of metadata
/// Stream header
public TablesStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader)
: this(mdReaderFactory, metadataBaseOffset, streamHeader, CLRRuntimeReaderKind.CLR) {
}
///
/// Constructor
///
/// factory
/// Offset of metadata
/// Stream header
/// Runtime kind
public TablesStream(DataReaderFactory mdReaderFactory, uint metadataBaseOffset, StreamHeader streamHeader, CLRRuntimeReaderKind runtime)
: base(mdReaderFactory, metadataBaseOffset, streamHeader) {
this.runtime = runtime;
}
///
/// Initializes MD tables
///
/// Type system table rows (from #Pdb stream)
public void Initialize(uint[] typeSystemTableRows) =>
Initialize(typeSystemTableRows, false);
///
/// Initializes MD tables
///
/// Type system table rows (from #Pdb stream)
/// Force all columns to 4 bytes instead of 2 or 4 bytes
internal void Initialize(uint[] typeSystemTableRows, bool forceAllBig) {
if (initialized)
throw new Exception("Initialize() has already been called");
initialized = true;
var reader = dataReader;
reserved1 = reader.ReadUInt32();
majorVersion = reader.ReadByte();
minorVersion = reader.ReadByte();
flags = (MDStreamFlags)reader.ReadByte();
log2Rid = reader.ReadByte();
validMask = reader.ReadUInt64();
sortedMask = reader.ReadUInt64();
// Mono assumes everything is sorted
if (runtime == CLRRuntimeReaderKind.Mono)
sortedMask = ulong.MaxValue;
var dnTableSizes = new DotNetTableSizes();
byte tmpMajor = majorVersion, tmpMinor = minorVersion;
// It ignores the version so use 2.0
if (runtime == CLRRuntimeReaderKind.Mono) {
tmpMajor = 2;
tmpMinor = 0;
}
var tableInfos = dnTableSizes.CreateTables(tmpMajor, tmpMinor, out int maxPresentTables);
if (typeSystemTableRows is not null)
maxPresentTables = DotNetTableSizes.normalMaxTables;
mdTables = new MDTable[tableInfos.Length];
ulong valid = validMask;
var sizes = new uint[64];
for (int i = 0; i < 64; valid >>= 1, i++) {
uint rows = (valid & 1) == 0 ? 0 : reader.ReadUInt32();
// Mono ignores the high byte
rows &= 0x00FFFFFF;
if (i >= maxPresentTables)
rows = 0;
sizes[i] = rows;
if (i < mdTables.Length)
mdTables[i] = new MDTable((Table)i, rows, tableInfos[i]);
}
if (HasExtraData)
extraData = reader.ReadUInt32();
var debugSizes = sizes;
if (typeSystemTableRows is not null) {
debugSizes = new uint[sizes.Length];
for (int i = 0; i < 64; i++) {
if (DotNetTableSizes.IsSystemTable((Table)i))
debugSizes[i] = typeSystemTableRows[i];
else
debugSizes[i] = sizes[i];
}
}
dnTableSizes.InitializeSizes(HasBigStrings, HasBigGUID, HasBigBlob, sizes, debugSizes, forceAllBig);
mdTablesPos = reader.Position;
InitializeMdTableReaders();
InitializeTables();
}
///
protected override void OnReaderRecreated() => InitializeMdTableReaders();
void InitializeMdTableReaders() {
var reader = dataReader;
reader.Position = mdTablesPos;
var currentPos = reader.Position;
foreach (var mdTable in mdTables) {
var dataLen = (uint)mdTable.TableInfo.RowSize * mdTable.Rows;
if (currentPos > reader.Length)
currentPos = reader.Length;
if ((ulong)currentPos + dataLen > reader.Length)
dataLen = reader.Length - currentPos;
mdTable.DataReader = reader.Slice(currentPos, dataLen);
var newPos = currentPos + dataLen;
if (newPos < currentPos)
throw new BadImageFormatException("Too big MD table");
currentPos = newPos;
}
}
void InitializeTables() {
ModuleTable = mdTables[(int)Table.Module];
TypeRefTable = mdTables[(int)Table.TypeRef];
TypeDefTable = mdTables[(int)Table.TypeDef];
FieldPtrTable = mdTables[(int)Table.FieldPtr];
FieldTable = mdTables[(int)Table.Field];
MethodPtrTable = mdTables[(int)Table.MethodPtr];
MethodTable = mdTables[(int)Table.Method];
ParamPtrTable = mdTables[(int)Table.ParamPtr];
ParamTable = mdTables[(int)Table.Param];
InterfaceImplTable = mdTables[(int)Table.InterfaceImpl];
MemberRefTable = mdTables[(int)Table.MemberRef];
ConstantTable = mdTables[(int)Table.Constant];
CustomAttributeTable = mdTables[(int)Table.CustomAttribute];
FieldMarshalTable = mdTables[(int)Table.FieldMarshal];
DeclSecurityTable = mdTables[(int)Table.DeclSecurity];
ClassLayoutTable = mdTables[(int)Table.ClassLayout];
FieldLayoutTable = mdTables[(int)Table.FieldLayout];
StandAloneSigTable = mdTables[(int)Table.StandAloneSig];
EventMapTable = mdTables[(int)Table.EventMap];
EventPtrTable = mdTables[(int)Table.EventPtr];
EventTable = mdTables[(int)Table.Event];
PropertyMapTable = mdTables[(int)Table.PropertyMap];
PropertyPtrTable = mdTables[(int)Table.PropertyPtr];
PropertyTable = mdTables[(int)Table.Property];
MethodSemanticsTable = mdTables[(int)Table.MethodSemantics];
MethodImplTable = mdTables[(int)Table.MethodImpl];
ModuleRefTable = mdTables[(int)Table.ModuleRef];
TypeSpecTable = mdTables[(int)Table.TypeSpec];
ImplMapTable = mdTables[(int)Table.ImplMap];
FieldRVATable = mdTables[(int)Table.FieldRVA];
ENCLogTable = mdTables[(int)Table.ENCLog];
ENCMapTable = mdTables[(int)Table.ENCMap];
AssemblyTable = mdTables[(int)Table.Assembly];
AssemblyProcessorTable = mdTables[(int)Table.AssemblyProcessor];
AssemblyOSTable = mdTables[(int)Table.AssemblyOS];
AssemblyRefTable = mdTables[(int)Table.AssemblyRef];
AssemblyRefProcessorTable = mdTables[(int)Table.AssemblyRefProcessor];
AssemblyRefOSTable = mdTables[(int)Table.AssemblyRefOS];
FileTable = mdTables[(int)Table.File];
ExportedTypeTable = mdTables[(int)Table.ExportedType];
ManifestResourceTable = mdTables[(int)Table.ManifestResource];
NestedClassTable = mdTables[(int)Table.NestedClass];
GenericParamTable = mdTables[(int)Table.GenericParam];
MethodSpecTable = mdTables[(int)Table.MethodSpec];
GenericParamConstraintTable = mdTables[(int)Table.GenericParamConstraint];
DocumentTable = mdTables[(int)Table.Document];
MethodDebugInformationTable = mdTables[(int)Table.MethodDebugInformation];
LocalScopeTable = mdTables[(int)Table.LocalScope];
LocalVariableTable = mdTables[(int)Table.LocalVariable];
LocalConstantTable = mdTables[(int)Table.LocalConstant];
ImportScopeTable = mdTables[(int)Table.ImportScope];
StateMachineMethodTable = mdTables[(int)Table.StateMachineMethod];
CustomDebugInformationTable = mdTables[(int)Table.CustomDebugInformation];
}
///
protected override void Dispose(bool disposing) {
if (disposing) {
var mt = mdTables;
if (mt is not null) {
foreach (var mdTable in mt) {
if (mdTable is not null)
mdTable.Dispose();
}
mdTables = null;
}
}
base.Dispose(disposing);
}
///
/// Returns a MD table
///
/// The table type
/// A or null if table doesn't exist
public MDTable Get(Table table) {
int index = (int)table;
if ((uint)index >= (uint)mdTables.Length)
return null;
return mdTables[index];
}
///
/// Checks whether a table exists
///
/// The table type
/// true if the table exists
public bool HasTable(Table table) => (uint)table < (uint)mdTables.Length;
///
/// Checks whether table is sorted
///
/// The table
public bool IsSorted(MDTable table) {
int index = (int)table.Table;
if ((uint)index >= 64)
return false;
return (sortedMask & (1UL << index)) != 0;
}
}
}