e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1370 lines
40 KiB
C#
1370 lines
40 KiB
C#
/*
|
|
Copyright (C) 2009-2013 Jeroen Frijters
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
Jeroen Frijters
|
|
jeroen@frijters.net
|
|
|
|
*/
|
|
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Collections.Generic;
|
|
using IKVM.Reflection.Metadata;
|
|
|
|
namespace IKVM.Reflection.Reader
|
|
{
|
|
sealed class StreamHeader
|
|
{
|
|
internal uint Offset;
|
|
internal uint Size;
|
|
internal string Name;
|
|
|
|
internal void Read(BinaryReader br)
|
|
{
|
|
Offset = br.ReadUInt32();
|
|
Size = br.ReadUInt32();
|
|
byte[] buf = new byte[32];
|
|
byte b;
|
|
int len = 0;
|
|
while ((b = br.ReadByte()) != 0)
|
|
{
|
|
buf[len++] = b;
|
|
}
|
|
Name = Encoding.UTF8.GetString(buf, 0, len); ;
|
|
int padding = -1 + ((len + 4) & ~3) - len;
|
|
br.BaseStream.Seek(padding, SeekOrigin.Current);
|
|
}
|
|
}
|
|
|
|
// FIXME: Put this somewhere else
|
|
class PdbStream {
|
|
public int EntryPoint { get; set; }
|
|
public ulong ReferencedTables { get; set; }
|
|
public int[] TableSizes { get; set; }
|
|
}
|
|
|
|
sealed class ModuleReader : Module
|
|
{
|
|
private readonly Stream stream;
|
|
private readonly string location;
|
|
private Assembly assembly;
|
|
private readonly PEReader peFile = new PEReader();
|
|
private readonly CliHeader cliHeader = new CliHeader();
|
|
private string imageRuntimeVersion;
|
|
private int metadataStreamVersion;
|
|
private byte[] stringHeap;
|
|
private byte[] blobHeap;
|
|
private byte[] guidHeap;
|
|
private uint userStringHeapOffset;
|
|
private uint userStringHeapSize;
|
|
private byte[] lazyUserStringHeap;
|
|
private TypeDefImpl[] typeDefs;
|
|
private TypeDefImpl moduleType;
|
|
private Assembly[] assemblyRefs;
|
|
private Type[] typeRefs;
|
|
private Type[] typeSpecs;
|
|
private FieldInfo[] fields;
|
|
private MethodBase[] methods;
|
|
private MemberInfo[] memberRefs;
|
|
private Dictionary<int, string> strings = new Dictionary<int, string>();
|
|
private Dictionary<TypeName, Type> types = new Dictionary<TypeName, Type>();
|
|
private Dictionary<TypeName, LazyForwardedType> forwardedTypes = new Dictionary<TypeName, LazyForwardedType>();
|
|
private bool isMetadataOnly;
|
|
|
|
private sealed class LazyForwardedType
|
|
{
|
|
private readonly int index;
|
|
private Type type;
|
|
|
|
internal LazyForwardedType(int index)
|
|
{
|
|
this.index = index;
|
|
}
|
|
|
|
internal Type GetType(ModuleReader module)
|
|
{
|
|
// guard against circular type forwarding
|
|
if (type == MarkerType.Pinned)
|
|
{
|
|
TypeName typeName = module.GetTypeName(module.ExportedType.records[index].TypeNamespace, module.ExportedType.records[index].TypeName);
|
|
return module.universe.GetMissingTypeOrThrow(module, module, null, typeName).SetCyclicTypeForwarder();
|
|
}
|
|
else if (type == null)
|
|
{
|
|
type = MarkerType.Pinned;
|
|
type = module.ResolveExportedType(index);
|
|
}
|
|
return type;
|
|
}
|
|
}
|
|
|
|
internal ModuleReader(AssemblyReader assembly, Universe universe, Stream stream, string location, bool mapped)
|
|
: base(universe)
|
|
{
|
|
this.stream = universe != null && universe.MetadataOnly ? null : stream;
|
|
this.location = location;
|
|
Read(stream, mapped);
|
|
if (universe != null && universe.WindowsRuntimeProjection && imageRuntimeVersion.StartsWith("WindowsRuntime ", StringComparison.Ordinal))
|
|
{
|
|
WindowsRuntimeProjection.Patch(this, strings, ref imageRuntimeVersion, ref blobHeap);
|
|
}
|
|
if (assembly == null && AssemblyTable.records.Length != 0)
|
|
{
|
|
assembly = new AssemblyReader(location, this);
|
|
}
|
|
this.assembly = assembly;
|
|
}
|
|
|
|
private void Read(Stream stream, bool mapped)
|
|
{
|
|
BinaryReader br = new BinaryReader(stream);
|
|
|
|
long pos = stream.Position;
|
|
uint header = br.ReadUInt32();
|
|
stream.Seek(pos, SeekOrigin.Begin);
|
|
|
|
if (header == 0x424a5342)
|
|
{
|
|
// Naked metadata file (enc/portable pdb)
|
|
this.isMetadataOnly = true;
|
|
}
|
|
else
|
|
{
|
|
peFile.Read(br, mapped);
|
|
stream.Seek(peFile.RvaToFileOffset(peFile.GetComDescriptorVirtualAddress()), SeekOrigin.Begin);
|
|
cliHeader.Read(br);
|
|
stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress), SeekOrigin.Begin);
|
|
}
|
|
foreach (StreamHeader sh in ReadStreamHeaders(br, out imageRuntimeVersion))
|
|
{
|
|
switch (sh.Name)
|
|
{
|
|
case "#Strings":
|
|
stringHeap = ReadHeap(stream, sh.Offset, sh.Size);
|
|
break;
|
|
case "#Blob":
|
|
blobHeap = ReadHeap(stream, sh.Offset, sh.Size);
|
|
break;
|
|
case "#US":
|
|
userStringHeapOffset = sh.Offset;
|
|
userStringHeapSize = sh.Size;
|
|
break;
|
|
case "#GUID":
|
|
guidHeap = ReadHeap(stream, sh.Offset, sh.Size);
|
|
break;
|
|
case "#~":
|
|
case "#-":
|
|
if (isMetadataOnly)
|
|
{
|
|
stream.Seek(sh.Offset, SeekOrigin.Begin);
|
|
}
|
|
else
|
|
{
|
|
stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress + sh.Offset), SeekOrigin.Begin);
|
|
}
|
|
ReadTables(br);
|
|
break;
|
|
case "#Pdb":
|
|
var entryPoint = br.ReadInt32 ();
|
|
var referencedTables = br.ReadUInt64 ();
|
|
var tableSizes = new int [64];
|
|
for (int i = 0; i < 64; ++i) {
|
|
if ((referencedTables & ((ulong)1 << i)) != 0)
|
|
tableSizes [i] = (int)br.ReadUInt32 ();
|
|
}
|
|
/*pdbStream =*/ new PdbStream () { EntryPoint = entryPoint, ReferencedTables = referencedTables, TableSizes = tableSizes };
|
|
break;
|
|
default:
|
|
// we ignore unknown streams, because the CLR does so too
|
|
// (and some obfuscators add bogus streams)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void SetAssembly(Assembly assembly)
|
|
{
|
|
this.assembly = assembly;
|
|
}
|
|
|
|
private static StreamHeader[] ReadStreamHeaders(BinaryReader br, out string Version)
|
|
{
|
|
uint Signature = br.ReadUInt32();
|
|
if (Signature != 0x424A5342)
|
|
{
|
|
throw new BadImageFormatException("Invalid metadata signature");
|
|
}
|
|
/*ushort MajorVersion =*/ br.ReadUInt16();
|
|
/*ushort MinorVersion =*/ br.ReadUInt16();
|
|
/*uint Reserved =*/ br.ReadUInt32();
|
|
uint Length = br.ReadUInt32();
|
|
byte[] buf = br.ReadBytes((int)Length);
|
|
Version = Encoding.UTF8.GetString(buf).TrimEnd('\u0000');
|
|
/*ushort Flags =*/ br.ReadUInt16();
|
|
ushort Streams = br.ReadUInt16();
|
|
StreamHeader[] streamHeaders = new StreamHeader[Streams];
|
|
for (int i = 0; i < streamHeaders.Length; i++)
|
|
{
|
|
streamHeaders[i] = new StreamHeader();
|
|
streamHeaders[i].Read(br);
|
|
}
|
|
return streamHeaders;
|
|
}
|
|
|
|
private void ReadTables(BinaryReader br)
|
|
{
|
|
Table[] tables = GetTables();
|
|
/*uint Reserved0 =*/ br.ReadUInt32();
|
|
byte MajorVersion = br.ReadByte();
|
|
byte MinorVersion = br.ReadByte();
|
|
metadataStreamVersion = MajorVersion << 16 | MinorVersion;
|
|
byte HeapSizes = br.ReadByte();
|
|
/*byte Reserved7 =*/ br.ReadByte();
|
|
ulong Valid = br.ReadUInt64();
|
|
ulong Sorted = br.ReadUInt64();
|
|
for (int i = 0; i < 64; i++)
|
|
{
|
|
if ((Valid & (1UL << i)) != 0)
|
|
{
|
|
if (tables[i] == null)
|
|
{
|
|
throw new NotImplementedException ("Unknown table " + i);
|
|
}
|
|
tables[i].Sorted = (Sorted & (1UL << i)) != 0;
|
|
tables[i].RowCount = br.ReadInt32();
|
|
}
|
|
}
|
|
MetadataReader mr = new MetadataReader(this, br.BaseStream, HeapSizes);
|
|
for (int i = 0; i < 64; i++)
|
|
{
|
|
if ((Valid & (1UL << i)) != 0)
|
|
{
|
|
tables[i].Read(mr);
|
|
}
|
|
}
|
|
if (ParamPtr.RowCount != 0)
|
|
{
|
|
throw new NotImplementedException("ParamPtr table support has not yet been implemented.");
|
|
}
|
|
}
|
|
|
|
private byte[] ReadHeap(Stream stream, uint offset, uint size)
|
|
{
|
|
byte[] buf = new byte[size];
|
|
if (isMetadataOnly)
|
|
{
|
|
stream.Seek(offset, SeekOrigin.Begin);
|
|
}
|
|
else
|
|
{
|
|
stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress + offset), SeekOrigin.Begin);
|
|
}
|
|
for (int pos = 0; pos < buf.Length; )
|
|
{
|
|
int read = stream.Read(buf, pos, buf.Length - pos);
|
|
if (read == 0)
|
|
{
|
|
throw new BadImageFormatException();
|
|
}
|
|
pos += read;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
internal void SeekRVA(int rva)
|
|
{
|
|
GetStream().Seek(peFile.RvaToFileOffset((uint)rva), SeekOrigin.Begin);
|
|
}
|
|
|
|
internal Stream GetStream()
|
|
{
|
|
if (stream == null)
|
|
{
|
|
throw new InvalidOperationException("Operation not available when UniverseOptions.MetadataOnly is enabled.");
|
|
}
|
|
return stream;
|
|
}
|
|
|
|
internal override void GetTypesImpl(List<Type> list)
|
|
{
|
|
PopulateTypeDef();
|
|
foreach (TypeDefImpl type in typeDefs)
|
|
{
|
|
if (type != moduleType)
|
|
{
|
|
list.Add(type);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PopulateTypeDef()
|
|
{
|
|
if (typeDefs == null)
|
|
{
|
|
typeDefs = new TypeDefImpl[TypeDef.records.Length];
|
|
for (int i = 0; i < typeDefs.Length; i++)
|
|
{
|
|
TypeDefImpl type = new TypeDefImpl(this, i);
|
|
typeDefs[i] = type;
|
|
if (type.IsModulePseudoType)
|
|
{
|
|
moduleType = type;
|
|
}
|
|
else if (!type.IsNestedByFlags)
|
|
{
|
|
types.Add(type.TypeName, type);
|
|
}
|
|
}
|
|
// add forwarded types to forwardedTypes dictionary (because Module.GetType(string) should return them)
|
|
for (int i = 0; i < ExportedType.records.Length; i++)
|
|
{
|
|
int implementation = ExportedType.records[i].Implementation;
|
|
if (implementation >> 24 == AssemblyRefTable.Index)
|
|
{
|
|
TypeName typeName = GetTypeName(ExportedType.records[i].TypeNamespace, ExportedType.records[i].TypeName);
|
|
forwardedTypes.Add(typeName, new LazyForwardedType(i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override string GetString(int index)
|
|
{
|
|
if (index == 0)
|
|
{
|
|
return null;
|
|
}
|
|
string str;
|
|
if (!strings.TryGetValue(index, out str))
|
|
{
|
|
int len = 0;
|
|
while (stringHeap[index + len] != 0)
|
|
{
|
|
len++;
|
|
}
|
|
str = Encoding.UTF8.GetString(stringHeap, index, len);
|
|
strings.Add(index, str);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
private static int ReadCompressedUInt(byte[] buffer, ref int offset)
|
|
{
|
|
byte b1 = buffer[offset++];
|
|
if (b1 <= 0x7F)
|
|
{
|
|
return b1;
|
|
}
|
|
else if ((b1 & 0xC0) == 0x80)
|
|
{
|
|
byte b2 = buffer[offset++];
|
|
return ((b1 & 0x3F) << 8) | b2;
|
|
}
|
|
else
|
|
{
|
|
byte b2 = buffer[offset++];
|
|
byte b3 = buffer[offset++];
|
|
byte b4 = buffer[offset++];
|
|
return ((b1 & 0x3F) << 24) + (b2 << 16) + (b3 << 8) + b4;
|
|
}
|
|
}
|
|
|
|
internal byte[] GetBlobCopy(int blobIndex)
|
|
{
|
|
int len = ReadCompressedUInt(blobHeap, ref blobIndex);
|
|
byte[] buf = new byte[len];
|
|
Buffer.BlockCopy(blobHeap, blobIndex, buf, 0, len);
|
|
return buf;
|
|
}
|
|
|
|
internal override ByteReader GetBlob(int blobIndex)
|
|
{
|
|
return ByteReader.FromBlob(blobHeap, blobIndex);
|
|
}
|
|
|
|
internal override Guid GetGuid(int guidIndex)
|
|
{
|
|
byte[] buf = new byte[16];
|
|
Buffer.BlockCopy(guidHeap, 16 * (guidIndex - 1), buf, 0, 16);
|
|
return new Guid(buf);
|
|
}
|
|
|
|
public override string ResolveString(int metadataToken)
|
|
{
|
|
string str;
|
|
if (!strings.TryGetValue(metadataToken, out str))
|
|
{
|
|
if ((metadataToken >> 24) != 0x70)
|
|
{
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
if (lazyUserStringHeap == null)
|
|
{
|
|
lazyUserStringHeap = ReadHeap(GetStream(), userStringHeapOffset, userStringHeapSize);
|
|
}
|
|
int index = metadataToken & 0xFFFFFF;
|
|
int len = ReadCompressedUInt(lazyUserStringHeap, ref index) & ~1;
|
|
StringBuilder sb = new StringBuilder(len / 2);
|
|
for (int i = 0; i < len; i += 2)
|
|
{
|
|
char ch = (char)(lazyUserStringHeap[index + i] | lazyUserStringHeap[index + i + 1] << 8);
|
|
sb.Append(ch);
|
|
}
|
|
str = sb.ToString();
|
|
strings.Add(metadataToken, str);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
internal override Type ResolveType(int metadataToken, IGenericContext context)
|
|
{
|
|
int index = (metadataToken & 0xFFFFFF) - 1;
|
|
if (index < 0)
|
|
{
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
else if ((metadataToken >> 24) == TypeDefTable.Index && index < TypeDef.RowCount)
|
|
{
|
|
PopulateTypeDef();
|
|
return typeDefs[index];
|
|
}
|
|
else if ((metadataToken >> 24) == TypeRefTable.Index && index < TypeRef.RowCount)
|
|
{
|
|
if (typeRefs == null)
|
|
{
|
|
typeRefs = new Type[TypeRef.records.Length];
|
|
}
|
|
if (typeRefs[index] == null)
|
|
{
|
|
int scope = TypeRef.records[index].ResolutionScope;
|
|
switch (scope >> 24)
|
|
{
|
|
case AssemblyRefTable.Index:
|
|
{
|
|
Assembly assembly = ResolveAssemblyRef((scope & 0xFFFFFF) - 1);
|
|
TypeName typeName = GetTypeName(TypeRef.records[index].TypeNamespace, TypeRef.records[index].TypeName);
|
|
typeRefs[index] = assembly.ResolveType(this, typeName);
|
|
break;
|
|
}
|
|
case TypeRefTable.Index:
|
|
{
|
|
Type outer = ResolveType(scope, null);
|
|
TypeName typeName = GetTypeName(TypeRef.records[index].TypeNamespace, TypeRef.records[index].TypeName);
|
|
typeRefs[index] = outer.ResolveNestedType(this, typeName);
|
|
break;
|
|
}
|
|
case ModuleTable.Index:
|
|
case ModuleRefTable.Index:
|
|
{
|
|
Module module;
|
|
if (scope >> 24 == ModuleTable.Index)
|
|
{
|
|
if (scope == 0 || scope == 1)
|
|
{
|
|
module = this;
|
|
}
|
|
else
|
|
{
|
|
throw new NotImplementedException("self reference scope?");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
module = ResolveModuleRef(ModuleRef.records[(scope & 0xFFFFFF) - 1]);
|
|
}
|
|
TypeName typeName = GetTypeName(TypeRef.records[index].TypeNamespace, TypeRef.records[index].TypeName);
|
|
typeRefs[index] = module.FindType(typeName) ?? module.universe.GetMissingTypeOrThrow(this, module, null, typeName);
|
|
break;
|
|
}
|
|
default:
|
|
throw new NotImplementedException("ResolutionScope = " + scope.ToString("X"));
|
|
}
|
|
}
|
|
return typeRefs[index];
|
|
}
|
|
else if ((metadataToken >> 24) == TypeSpecTable.Index && index < TypeSpec.RowCount)
|
|
{
|
|
if (typeSpecs == null)
|
|
{
|
|
typeSpecs = new Type[TypeSpec.records.Length];
|
|
}
|
|
Type type = typeSpecs[index];
|
|
if (type == null)
|
|
{
|
|
TrackingGenericContext tc = context == null ? null : new TrackingGenericContext(context);
|
|
type = Signature.ReadTypeSpec(this, ByteReader.FromBlob(blobHeap, TypeSpec.records[index]), tc);
|
|
if (tc == null || !tc.IsUsed)
|
|
{
|
|
typeSpecs[index] = type;
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
else
|
|
{
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
}
|
|
|
|
private Module ResolveModuleRef(int moduleNameIndex)
|
|
{
|
|
string moduleName = GetString(moduleNameIndex);
|
|
Module module = assembly.GetModule(moduleName);
|
|
if (module == null)
|
|
{
|
|
throw new FileNotFoundException(moduleName);
|
|
}
|
|
return module;
|
|
}
|
|
|
|
private sealed class TrackingGenericContext : IGenericContext
|
|
{
|
|
private readonly IGenericContext context;
|
|
private bool used;
|
|
|
|
internal TrackingGenericContext(IGenericContext context)
|
|
{
|
|
this.context = context;
|
|
}
|
|
|
|
internal bool IsUsed
|
|
{
|
|
get { return used; }
|
|
}
|
|
|
|
public Type GetGenericTypeArgument(int index)
|
|
{
|
|
used = true;
|
|
return context.GetGenericTypeArgument(index);
|
|
}
|
|
|
|
public Type GetGenericMethodArgument(int index)
|
|
{
|
|
used = true;
|
|
return context.GetGenericMethodArgument(index);
|
|
}
|
|
}
|
|
|
|
private TypeName GetTypeName(int typeNamespace, int typeName)
|
|
{
|
|
return new TypeName(GetString(typeNamespace), GetString(typeName));
|
|
}
|
|
|
|
internal Assembly ResolveAssemblyRef(int index)
|
|
{
|
|
if (assemblyRefs == null)
|
|
{
|
|
assemblyRefs = new Assembly[AssemblyRef.RowCount];
|
|
}
|
|
if (assemblyRefs[index] == null)
|
|
{
|
|
assemblyRefs[index] = ResolveAssemblyRefImpl(ref AssemblyRef.records[index]);
|
|
}
|
|
return assemblyRefs[index];
|
|
}
|
|
|
|
private Assembly ResolveAssemblyRefImpl(ref AssemblyRefTable.Record rec)
|
|
{
|
|
const int PublicKey = 0x0001;
|
|
string name = AssemblyName.GetFullName(
|
|
GetString(rec.Name),
|
|
rec.MajorVersion,
|
|
rec.MinorVersion,
|
|
rec.BuildNumber,
|
|
rec.RevisionNumber,
|
|
rec.Culture == 0 ? "neutral" : GetString(rec.Culture),
|
|
rec.PublicKeyOrToken == 0 ? Empty<byte>.Array : (rec.Flags & PublicKey) == 0 ? GetBlobCopy(rec.PublicKeyOrToken) : AssemblyName.ComputePublicKeyToken(GetBlobCopy(rec.PublicKeyOrToken)),
|
|
rec.Flags);
|
|
return universe.Load(name, this, true);
|
|
}
|
|
|
|
public override Guid ModuleVersionId
|
|
{
|
|
get
|
|
{
|
|
byte[] buf = new byte[16];
|
|
Buffer.BlockCopy(guidHeap, 16 * (ModuleTable.records[0].Mvid - 1), buf, 0, 16);
|
|
return new Guid(buf);
|
|
}
|
|
}
|
|
|
|
public override string FullyQualifiedName
|
|
{
|
|
get { return location ?? "<Unknown>"; }
|
|
}
|
|
|
|
public override string Name
|
|
{
|
|
get { return location == null ? "<Unknown>" : System.IO.Path.GetFileName(location); }
|
|
}
|
|
|
|
public override Assembly Assembly
|
|
{
|
|
get { return assembly; }
|
|
}
|
|
|
|
internal override Type FindType(TypeName typeName)
|
|
{
|
|
PopulateTypeDef();
|
|
Type type;
|
|
if (!types.TryGetValue(typeName, out type))
|
|
{
|
|
LazyForwardedType fw;
|
|
if (forwardedTypes.TryGetValue(typeName, out fw))
|
|
{
|
|
return fw.GetType(this);
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
|
|
internal override Type FindTypeIgnoreCase(TypeName lowerCaseName)
|
|
{
|
|
PopulateTypeDef();
|
|
foreach (Type type in types.Values)
|
|
{
|
|
if (type.TypeName.ToLowerInvariant() == lowerCaseName)
|
|
{
|
|
return type;
|
|
}
|
|
}
|
|
foreach (TypeName name in forwardedTypes.Keys)
|
|
{
|
|
if (name.ToLowerInvariant() == lowerCaseName)
|
|
{
|
|
return forwardedTypes[name].GetType(this);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private Exception TokenOutOfRangeException(int metadataToken)
|
|
{
|
|
return new ArgumentOutOfRangeException("metadataToken", String.Format("Token 0x{0:x8} is not valid in the scope of module {1}.", metadataToken, this.Name));
|
|
}
|
|
|
|
public override MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
|
|
{
|
|
switch (metadataToken >> 24)
|
|
{
|
|
case FieldTable.Index:
|
|
return ResolveField(metadataToken, genericTypeArguments, genericMethodArguments);
|
|
case MemberRefTable.Index:
|
|
int index = (metadataToken & 0xFFFFFF) - 1;
|
|
if (index < 0 || index >= MemberRef.RowCount)
|
|
{
|
|
goto default;
|
|
}
|
|
return GetMemberRef(index, genericTypeArguments, genericMethodArguments);
|
|
case MethodDefTable.Index:
|
|
case MethodSpecTable.Index:
|
|
return ResolveMethod(metadataToken, genericTypeArguments, genericMethodArguments);
|
|
case TypeRefTable.Index:
|
|
case TypeDefTable.Index:
|
|
case TypeSpecTable.Index:
|
|
return ResolveType(metadataToken, genericTypeArguments, genericMethodArguments);
|
|
default:
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
}
|
|
|
|
internal FieldInfo GetFieldAt(TypeDefImpl owner, int index)
|
|
{
|
|
if (fields == null)
|
|
{
|
|
fields = new FieldInfo[Field.records.Length];
|
|
}
|
|
if (fields[index] == null)
|
|
{
|
|
fields[index] = new FieldDefImpl(this, owner ?? FindFieldOwner(index), index);
|
|
}
|
|
return fields[index];
|
|
}
|
|
|
|
public override FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
|
|
{
|
|
int index = (metadataToken & 0xFFFFFF) - 1;
|
|
if (index < 0)
|
|
{
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
else if ((metadataToken >> 24) == FieldTable.Index && index < Field.RowCount)
|
|
{
|
|
return GetFieldAt(null, index);
|
|
}
|
|
else if ((metadataToken >> 24) == MemberRefTable.Index && index < MemberRef.RowCount)
|
|
{
|
|
FieldInfo field = GetMemberRef(index, genericTypeArguments, genericMethodArguments) as FieldInfo;
|
|
if (field != null)
|
|
{
|
|
return field;
|
|
}
|
|
throw new ArgumentException(String.Format("Token 0x{0:x8} is not a valid FieldInfo token in the scope of module {1}.", metadataToken, this.Name), "metadataToken");
|
|
}
|
|
else
|
|
{
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
}
|
|
|
|
private TypeDefImpl FindFieldOwner(int fieldIndex)
|
|
{
|
|
// TODO use binary search?
|
|
for (int i = 0; i < TypeDef.records.Length; i++)
|
|
{
|
|
int field = TypeDef.records[i].FieldList - 1;
|
|
int end = TypeDef.records.Length > i + 1 ? TypeDef.records[i + 1].FieldList - 1 : Field.records.Length;
|
|
if (field <= fieldIndex && fieldIndex < end)
|
|
{
|
|
PopulateTypeDef();
|
|
return typeDefs[i];
|
|
}
|
|
}
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
internal MethodBase GetMethodAt(TypeDefImpl owner, int index)
|
|
{
|
|
if (methods == null)
|
|
{
|
|
methods = new MethodBase[MethodDef.records.Length];
|
|
}
|
|
if (methods[index] == null)
|
|
{
|
|
MethodDefImpl method = new MethodDefImpl(this, owner ?? FindMethodOwner(index), index);
|
|
methods[index] = method.IsConstructor ? new ConstructorInfoImpl(method) : (MethodBase)method;
|
|
}
|
|
return methods[index];
|
|
}
|
|
|
|
public override MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
|
|
{
|
|
int index = (metadataToken & 0xFFFFFF) - 1;
|
|
if (index < 0)
|
|
{
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
else if ((metadataToken >> 24) == MethodDefTable.Index && index < MethodDef.RowCount)
|
|
{
|
|
return GetMethodAt(null, index);
|
|
}
|
|
else if ((metadataToken >> 24) == MemberRefTable.Index && index < MemberRef.RowCount)
|
|
{
|
|
MethodBase method = GetMemberRef(index, genericTypeArguments, genericMethodArguments) as MethodBase;
|
|
if (method != null)
|
|
{
|
|
return method;
|
|
}
|
|
throw new ArgumentException(String.Format("Token 0x{0:x8} is not a valid MethodBase token in the scope of module {1}.", metadataToken, this.Name), "metadataToken");
|
|
}
|
|
else if ((metadataToken >> 24) == MethodSpecTable.Index && index < MethodSpec.RowCount)
|
|
{
|
|
MethodInfo method = (MethodInfo)ResolveMethod(MethodSpec.records[index].Method, genericTypeArguments, genericMethodArguments);
|
|
ByteReader instantiation = ByteReader.FromBlob(blobHeap, MethodSpec.records[index].Instantiation);
|
|
return method.MakeGenericMethod(Signature.ReadMethodSpec(this, instantiation, new GenericContext(genericTypeArguments, genericMethodArguments)));
|
|
}
|
|
else
|
|
{
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
}
|
|
|
|
public override Type[] __ResolveOptionalParameterTypes(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments, out CustomModifiers[] customModifiers)
|
|
{
|
|
int index = (metadataToken & 0xFFFFFF) - 1;
|
|
if (index < 0)
|
|
{
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
else if ((metadataToken >> 24) == MemberRefTable.Index && index < MemberRef.RowCount)
|
|
{
|
|
int sig = MemberRef.records[index].Signature;
|
|
return Signature.ReadOptionalParameterTypes(this, GetBlob(sig), new GenericContext(genericTypeArguments, genericMethodArguments), out customModifiers);
|
|
}
|
|
else if ((metadataToken >> 24) == MethodDefTable.Index && index < MethodDef.RowCount)
|
|
{
|
|
// for convenience, we support passing a MethodDef token as well, because in some places
|
|
// it makes sense to have a vararg method that is referred to by its methoddef (e.g. ldftn).
|
|
// Note that MethodSpec doesn't make sense, because generic methods cannot be vararg.
|
|
customModifiers = Empty<CustomModifiers>.Array;
|
|
return Type.EmptyTypes;
|
|
}
|
|
else
|
|
{
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
}
|
|
|
|
public override CustomModifiers __ResolveTypeSpecCustomModifiers(int typeSpecToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
|
|
{
|
|
int index = (typeSpecToken & 0xFFFFFF) - 1;
|
|
if (typeSpecToken >> 24 != TypeSpecTable.Index || index < 0 || index >= TypeSpec.RowCount)
|
|
{
|
|
throw TokenOutOfRangeException(typeSpecToken);
|
|
}
|
|
return CustomModifiers.Read(this, ByteReader.FromBlob(blobHeap, TypeSpec.records[index]), new GenericContext(genericTypeArguments, genericMethodArguments));
|
|
}
|
|
|
|
public override string ScopeName
|
|
{
|
|
get { return GetString(ModuleTable.records[0].Name); }
|
|
}
|
|
|
|
private TypeDefImpl FindMethodOwner(int methodIndex)
|
|
{
|
|
// TODO use binary search?
|
|
for (int i = 0; i < TypeDef.records.Length; i++)
|
|
{
|
|
int method = TypeDef.records[i].MethodList - 1;
|
|
int end = TypeDef.records.Length > i + 1 ? TypeDef.records[i + 1].MethodList - 1 : MethodDef.records.Length;
|
|
if (method <= methodIndex && methodIndex < end)
|
|
{
|
|
PopulateTypeDef();
|
|
return typeDefs[i];
|
|
}
|
|
}
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
private MemberInfo GetMemberRef(int index, Type[] genericTypeArguments, Type[] genericMethodArguments)
|
|
{
|
|
if (memberRefs == null)
|
|
{
|
|
memberRefs = new MemberInfo[MemberRef.records.Length];
|
|
}
|
|
if (memberRefs[index] == null)
|
|
{
|
|
int owner = MemberRef.records[index].Class;
|
|
int sig = MemberRef.records[index].Signature;
|
|
string name = GetString(MemberRef.records[index].Name);
|
|
switch (owner >> 24)
|
|
{
|
|
case MethodDefTable.Index:
|
|
return GetMethodAt(null, (owner & 0xFFFFFF) - 1);
|
|
case ModuleRefTable.Index:
|
|
memberRefs[index] = ResolveTypeMemberRef(ResolveModuleType(owner), name, ByteReader.FromBlob(blobHeap, sig));
|
|
break;
|
|
case TypeDefTable.Index:
|
|
case TypeRefTable.Index:
|
|
memberRefs[index] = ResolveTypeMemberRef(ResolveType(owner), name, ByteReader.FromBlob(blobHeap, sig));
|
|
break;
|
|
case TypeSpecTable.Index:
|
|
{
|
|
Type type = ResolveType(owner, genericTypeArguments, genericMethodArguments);
|
|
if (type.IsArray)
|
|
{
|
|
MethodSignature methodSig = MethodSignature.ReadSig(this, ByteReader.FromBlob(blobHeap, sig), new GenericContext(genericTypeArguments, genericMethodArguments));
|
|
return type.FindMethod(name, methodSig)
|
|
?? universe.GetMissingMethodOrThrow(this, type, name, methodSig);
|
|
}
|
|
else if (type.IsConstructedGenericType)
|
|
{
|
|
MemberInfo member = ResolveTypeMemberRef(type.GetGenericTypeDefinition(), name, ByteReader.FromBlob(blobHeap, sig));
|
|
MethodBase mb = member as MethodBase;
|
|
if (mb != null)
|
|
{
|
|
member = mb.BindTypeParameters(type);
|
|
}
|
|
FieldInfo fi = member as FieldInfo;
|
|
if (fi != null)
|
|
{
|
|
member = fi.BindTypeParameters(type);
|
|
}
|
|
return member;
|
|
}
|
|
else
|
|
{
|
|
return ResolveTypeMemberRef(type, name, ByteReader.FromBlob(blobHeap, sig));
|
|
}
|
|
}
|
|
default:
|
|
throw new BadImageFormatException();
|
|
}
|
|
}
|
|
return memberRefs[index];
|
|
}
|
|
|
|
private Type ResolveModuleType(int token)
|
|
{
|
|
int index = (token & 0xFFFFFF) - 1;
|
|
string name = GetString(ModuleRef.records[index]);
|
|
Module module = assembly.GetModule(name);
|
|
if (module == null || module.IsResource())
|
|
{
|
|
throw new BadImageFormatException();
|
|
}
|
|
return module.GetModuleType();
|
|
}
|
|
|
|
private MemberInfo ResolveTypeMemberRef(Type type, string name, ByteReader sig)
|
|
{
|
|
if (sig.PeekByte() == Signature.FIELD)
|
|
{
|
|
Type org = type;
|
|
FieldSignature fieldSig = FieldSignature.ReadSig(this, sig, type);
|
|
FieldInfo field = type.FindField(name, fieldSig);
|
|
if (field == null && universe.MissingMemberResolution)
|
|
{
|
|
return universe.GetMissingFieldOrThrow(this, type, name, fieldSig);
|
|
}
|
|
while (field == null && (type = type.BaseType) != null)
|
|
{
|
|
field = type.FindField(name, fieldSig);
|
|
}
|
|
if (field != null)
|
|
{
|
|
return field;
|
|
}
|
|
#if CORECLR
|
|
throw new MissingFieldException(org.ToString() + "." + name);
|
|
#else
|
|
throw new MissingFieldException(org.ToString(), name);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
Type org = type;
|
|
MethodSignature methodSig = MethodSignature.ReadSig(this, sig, type);
|
|
MethodBase method = type.FindMethod(name, methodSig);
|
|
if (method == null && universe.MissingMemberResolution)
|
|
{
|
|
return universe.GetMissingMethodOrThrow(this, type, name, methodSig);
|
|
}
|
|
while (method == null && (type = type.BaseType) != null)
|
|
{
|
|
method = type.FindMethod(name, methodSig);
|
|
}
|
|
if (method != null)
|
|
{
|
|
return method;
|
|
}
|
|
#if CORECLR
|
|
throw new MissingMethodException(org.ToString() + "." + name);
|
|
#else
|
|
throw new MissingMethodException(org.ToString(), name);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
internal ByteReader GetStandAloneSig(int index)
|
|
{
|
|
return ByteReader.FromBlob(blobHeap, StandAloneSig.records[index]);
|
|
}
|
|
|
|
public override byte[] ResolveSignature(int metadataToken)
|
|
{
|
|
int index = (metadataToken & 0xFFFFFF) - 1;
|
|
if ((metadataToken >> 24) == StandAloneSigTable.Index && index >= 0 && index < StandAloneSig.RowCount)
|
|
{
|
|
ByteReader br = GetStandAloneSig(index);
|
|
return br.ReadBytes(br.Length);
|
|
}
|
|
else
|
|
{
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
}
|
|
|
|
public override __StandAloneMethodSig __ResolveStandAloneMethodSig(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
|
|
{
|
|
int index = (metadataToken & 0xFFFFFF) - 1;
|
|
if ((metadataToken >> 24) == StandAloneSigTable.Index && index >= 0 && index < StandAloneSig.RowCount)
|
|
{
|
|
return MethodSignature.ReadStandAloneMethodSig(this, GetStandAloneSig(index), new GenericContext(genericTypeArguments, genericMethodArguments));
|
|
}
|
|
else
|
|
{
|
|
throw TokenOutOfRangeException(metadataToken);
|
|
}
|
|
}
|
|
|
|
internal MethodInfo GetEntryPoint()
|
|
{
|
|
if (cliHeader.EntryPointToken != 0 && (cliHeader.Flags & CliHeader.COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) == 0)
|
|
{
|
|
return (MethodInfo)ResolveMethod((int)cliHeader.EntryPointToken);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
internal string[] GetManifestResourceNames()
|
|
{
|
|
string[] names = new string[ManifestResource.records.Length];
|
|
for (int i = 0; i < ManifestResource.records.Length; i++)
|
|
{
|
|
names[i] = GetString(ManifestResource.records[i].Name);
|
|
}
|
|
return names;
|
|
}
|
|
|
|
internal ManifestResourceInfo GetManifestResourceInfo(string resourceName)
|
|
{
|
|
for (int i = 0; i < ManifestResource.records.Length; i++)
|
|
{
|
|
if (resourceName == GetString(ManifestResource.records[i].Name))
|
|
{
|
|
ManifestResourceInfo info = new ManifestResourceInfo(this, i);
|
|
Assembly asm = info.ReferencedAssembly;
|
|
if (asm != null && !asm.__IsMissing && asm.GetManifestResourceInfo(resourceName) == null)
|
|
{
|
|
return null;
|
|
}
|
|
return info;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
internal Stream GetManifestResourceStream(string resourceName)
|
|
{
|
|
for (int i = 0; i < ManifestResource.records.Length; i++)
|
|
{
|
|
if (resourceName == GetString(ManifestResource.records[i].Name))
|
|
{
|
|
if (ManifestResource.records[i].Implementation != 0x26000000)
|
|
{
|
|
ManifestResourceInfo info = new ManifestResourceInfo(this, i);
|
|
switch (ManifestResource.records[i].Implementation >> 24)
|
|
{
|
|
case FileTable.Index:
|
|
string fileName = Path.Combine(Path.GetDirectoryName(location), info.FileName);
|
|
if (System.IO.File.Exists(fileName))
|
|
{
|
|
// note that, like System.Reflection, we return null for zero length files and
|
|
// ManifestResource.Offset is ignored
|
|
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
|
|
if (fs.Length == 0)
|
|
{
|
|
fs.Close();
|
|
return null;
|
|
}
|
|
return fs;
|
|
}
|
|
return null;
|
|
case AssemblyRefTable.Index:
|
|
Assembly asm = info.ReferencedAssembly;
|
|
if (asm.__IsMissing)
|
|
{
|
|
return null;
|
|
}
|
|
return asm.GetManifestResourceStream(resourceName);
|
|
default:
|
|
throw new BadImageFormatException();
|
|
}
|
|
}
|
|
SeekRVA((int)cliHeader.Resources.VirtualAddress + ManifestResource.records[i].Offset);
|
|
BinaryReader br = new BinaryReader(stream);
|
|
int length = br.ReadInt32();
|
|
return new MemoryStream(br.ReadBytes(length));
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public override AssemblyName[] __GetReferencedAssemblies()
|
|
{
|
|
List<AssemblyName> list = new List<AssemblyName>();
|
|
for (int i = 0; i < AssemblyRef.records.Length; i++)
|
|
{
|
|
AssemblyName name = new AssemblyName();
|
|
name.Name = GetString(AssemblyRef.records[i].Name);
|
|
name.Version = new Version(
|
|
AssemblyRef.records[i].MajorVersion,
|
|
AssemblyRef.records[i].MinorVersion,
|
|
AssemblyRef.records[i].BuildNumber,
|
|
AssemblyRef.records[i].RevisionNumber);
|
|
if (AssemblyRef.records[i].PublicKeyOrToken != 0)
|
|
{
|
|
byte[] keyOrToken = GetBlobCopy(AssemblyRef.records[i].PublicKeyOrToken);
|
|
const int PublicKey = 0x0001;
|
|
if ((AssemblyRef.records[i].Flags & PublicKey) != 0)
|
|
{
|
|
name.SetPublicKey(keyOrToken);
|
|
}
|
|
else
|
|
{
|
|
name.SetPublicKeyToken(keyOrToken);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
name.SetPublicKeyToken(Empty<byte>.Array);
|
|
}
|
|
if (AssemblyRef.records[i].Culture != 0)
|
|
{
|
|
name.Culture = GetString(AssemblyRef.records[i].Culture);
|
|
}
|
|
else
|
|
{
|
|
name.Culture = "";
|
|
}
|
|
if (AssemblyRef.records[i].HashValue != 0)
|
|
{
|
|
name.hash = GetBlobCopy(AssemblyRef.records[i].HashValue);
|
|
}
|
|
name.RawFlags = (AssemblyNameFlags)AssemblyRef.records[i].Flags;
|
|
list.Add(name);
|
|
}
|
|
return list.ToArray();
|
|
}
|
|
|
|
public override void __ResolveReferencedAssemblies(Assembly[] assemblies)
|
|
{
|
|
if (assemblyRefs == null)
|
|
{
|
|
assemblyRefs = new Assembly[AssemblyRef.RowCount];
|
|
}
|
|
for (int i = 0; i < assemblies.Length; i++)
|
|
{
|
|
if (assemblyRefs[i] == null)
|
|
{
|
|
assemblyRefs[i] = assemblies[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
public override string[] __GetReferencedModules()
|
|
{
|
|
string[] arr = new string[this.ModuleRef.RowCount];
|
|
for (int i = 0; i < arr.Length; i++)
|
|
{
|
|
arr[i] = GetString(this.ModuleRef.records[i]);
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
public override Type[] __GetReferencedTypes()
|
|
{
|
|
Type[] arr = new Type[this.TypeRef.RowCount];
|
|
for (int i = 0; i < arr.Length; i++)
|
|
{
|
|
arr[i] = ResolveType((TypeRefTable.Index << 24) + i + 1);
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
public override Type[] __GetExportedTypes()
|
|
{
|
|
Type[] arr = new Type[this.ExportedType.RowCount];
|
|
for (int i = 0; i < arr.Length; i++)
|
|
{
|
|
arr[i] = ResolveExportedType(i);
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
private Type ResolveExportedType(int index)
|
|
{
|
|
TypeName typeName = GetTypeName(ExportedType.records[index].TypeNamespace, ExportedType.records[index].TypeName);
|
|
int implementation = ExportedType.records[index].Implementation;
|
|
int token = ExportedType.records[index].TypeDefId;
|
|
int flags = ExportedType.records[index].Flags;
|
|
switch (implementation >> 24)
|
|
{
|
|
case AssemblyRefTable.Index:
|
|
return ResolveAssemblyRef((implementation & 0xFFFFFF) - 1).ResolveType(this, typeName).SetMetadataTokenForMissing(token, flags);
|
|
case ExportedTypeTable.Index:
|
|
return ResolveExportedType((implementation & 0xFFFFFF) - 1).ResolveNestedType(this, typeName).SetMetadataTokenForMissing(token, flags);
|
|
case FileTable.Index:
|
|
Module module = assembly.GetModule(GetString(File.records[(implementation & 0xFFFFFF) - 1].Name));
|
|
return module.FindType(typeName) ?? module.universe.GetMissingTypeOrThrow(this, module, null, typeName).SetMetadataTokenForMissing(token, flags);
|
|
default:
|
|
throw new BadImageFormatException();
|
|
}
|
|
}
|
|
|
|
internal override Type GetModuleType()
|
|
{
|
|
PopulateTypeDef();
|
|
return moduleType;
|
|
}
|
|
|
|
public override string __ImageRuntimeVersion
|
|
{
|
|
get { return imageRuntimeVersion; }
|
|
}
|
|
|
|
public override int MDStreamVersion
|
|
{
|
|
get { return metadataStreamVersion; }
|
|
}
|
|
|
|
public override bool __IsMetadataOnly
|
|
{
|
|
get { return isMetadataOnly; }
|
|
}
|
|
|
|
public override void __GetDataDirectoryEntry(int index, out int rva, out int length)
|
|
{
|
|
if (isMetadataOnly)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
peFile.GetDataDirectoryEntry(index, out rva, out length);
|
|
}
|
|
|
|
public override long __RelativeVirtualAddressToFileOffset(int rva)
|
|
{
|
|
return peFile.RvaToFileOffset((uint)rva);
|
|
}
|
|
|
|
public override bool __GetSectionInfo(int rva, out string name, out int characteristics, out int virtualAddress, out int virtualSize, out int pointerToRawData, out int sizeOfRawData)
|
|
{
|
|
return peFile.GetSectionInfo(rva, out name, out characteristics, out virtualAddress, out virtualSize, out pointerToRawData, out sizeOfRawData);
|
|
}
|
|
|
|
public override int __ReadDataFromRVA(int rva, byte[] data, int offset, int length)
|
|
{
|
|
SeekRVA(rva);
|
|
int totalBytesRead = 0;
|
|
while (length > 0)
|
|
{
|
|
int read = stream.Read(data, offset, length);
|
|
if (read == 0)
|
|
{
|
|
// C++ assemblies can have fields that have an RVA that lies outside of the file
|
|
break;
|
|
}
|
|
offset += read;
|
|
length -= read;
|
|
totalBytesRead += read;
|
|
}
|
|
return totalBytesRead;
|
|
}
|
|
|
|
public override void GetPEKind(out PortableExecutableKinds peKind, out ImageFileMachine machine)
|
|
{
|
|
peKind = 0;
|
|
if ((cliHeader.Flags & CliHeader.COMIMAGE_FLAGS_ILONLY) != 0)
|
|
{
|
|
peKind |= PortableExecutableKinds.ILOnly;
|
|
}
|
|
switch (cliHeader.Flags & (CliHeader.COMIMAGE_FLAGS_32BITREQUIRED | CliHeader.COMIMAGE_FLAGS_32BITPREFERRED))
|
|
{
|
|
case CliHeader.COMIMAGE_FLAGS_32BITREQUIRED:
|
|
peKind |= PortableExecutableKinds.Required32Bit;
|
|
break;
|
|
case CliHeader.COMIMAGE_FLAGS_32BITREQUIRED | CliHeader.COMIMAGE_FLAGS_32BITPREFERRED:
|
|
peKind |= PortableExecutableKinds.Preferred32Bit;
|
|
break;
|
|
default:
|
|
// COMIMAGE_FLAGS_32BITPREFERRED by itself is illegal, so we ignore it
|
|
// (not setting any flag is ok)
|
|
break;
|
|
}
|
|
if (peFile.OptionalHeader.Magic == IMAGE_OPTIONAL_HEADER.IMAGE_NT_OPTIONAL_HDR64_MAGIC)
|
|
{
|
|
peKind |= PortableExecutableKinds.PE32Plus;
|
|
}
|
|
|
|
machine = (ImageFileMachine)peFile.FileHeader.Machine;
|
|
}
|
|
|
|
public override int __Subsystem
|
|
{
|
|
get { return peFile.OptionalHeader.Subsystem; }
|
|
}
|
|
|
|
public override IList<CustomAttributeData> __GetPlaceholderAssemblyCustomAttributes(bool multiple, bool security)
|
|
{
|
|
TypeName typeName;
|
|
switch ((multiple ? 1 : 0) + (security ? 2 : 0))
|
|
{
|
|
case 0:
|
|
typeName = new TypeName("System.Runtime.CompilerServices", "AssemblyAttributesGoHere");
|
|
break;
|
|
case 1:
|
|
typeName = new TypeName("System.Runtime.CompilerServices", "AssemblyAttributesGoHereM");
|
|
break;
|
|
case 2:
|
|
typeName = new TypeName("System.Runtime.CompilerServices", "AssemblyAttributesGoHereS");
|
|
break;
|
|
case 3:
|
|
default:
|
|
typeName = new TypeName("System.Runtime.CompilerServices", "AssemblyAttributesGoHereSM");
|
|
break;
|
|
}
|
|
List<CustomAttributeData> list = new List<CustomAttributeData>();
|
|
for (int i = 0; i < CustomAttribute.records.Length; i++)
|
|
{
|
|
if ((CustomAttribute.records[i].Parent >> 24) == TypeRefTable.Index)
|
|
{
|
|
int index = (CustomAttribute.records[i].Parent & 0xFFFFFF) - 1;
|
|
if (typeName == GetTypeName(TypeRef.records[index].TypeNamespace, TypeRef.records[index].TypeName))
|
|
{
|
|
list.Add(new CustomAttributeData(this, i));
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
internal override void Dispose()
|
|
{
|
|
if (stream != null)
|
|
{
|
|
stream.Close();
|
|
}
|
|
}
|
|
|
|
internal override void ExportTypes(int fileToken, IKVM.Reflection.Emit.ModuleBuilder manifestModule)
|
|
{
|
|
PopulateTypeDef();
|
|
manifestModule.ExportTypes(typeDefs, fileToken);
|
|
}
|
|
|
|
protected override long GetImageBaseImpl()
|
|
{
|
|
return (long)peFile.OptionalHeader.ImageBase;
|
|
}
|
|
|
|
protected override long GetStackReserveImpl()
|
|
{
|
|
return (long)peFile.OptionalHeader.SizeOfStackReserve;
|
|
}
|
|
|
|
protected override int GetFileAlignmentImpl()
|
|
{
|
|
return (int)peFile.OptionalHeader.FileAlignment;
|
|
}
|
|
|
|
protected override DllCharacteristics GetDllCharacteristicsImpl()
|
|
{
|
|
return (DllCharacteristics)peFile.OptionalHeader.DllCharacteristics;
|
|
}
|
|
|
|
public override int __EntryPointRVA
|
|
{
|
|
get { return (cliHeader.Flags & CliHeader.COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) != 0 ? (int)cliHeader.EntryPointToken : 0; }
|
|
}
|
|
|
|
public override int __EntryPointToken
|
|
{
|
|
get { return (cliHeader.Flags & CliHeader.COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) == 0 ? (int)cliHeader.EntryPointToken : 0; }
|
|
}
|
|
|
|
#if !NO_AUTHENTICODE
|
|
public override System.Security.Cryptography.X509Certificates.X509Certificate GetSignerCertificate()
|
|
{
|
|
return Authenticode.GetSignerCertificate(GetStream());
|
|
}
|
|
#endif // !NO_AUTHENTICODE
|
|
}
|
|
}
|