diff options
author | acmyu <amycmyu@gmail.com> | 2018-05-23 17:24:03 -0700 |
---|---|---|
committer | Zach Montoya <zamont@microsoft.com> | 2018-05-23 17:24:03 -0700 |
commit | 6c934c21df759d4319e2006ef4add9e01068f648 (patch) | |
tree | a659e52bfda8b57c85523426aa3c066473cf0c71 /src | |
parent | f4c6bebb1601596ea54baff489e50b64ad4a6a2e (diff) | |
download | coreclr-6c934c21df759d4319e2006ef4add9e01068f648.tar.gz coreclr-6c934c21df759d4319e2006ef4add9e01068f648.tar.bz2 coreclr-6c934c21df759d4319e2006ef4add9e01068f648.zip |
R2RDump - Dump generic method instances (#18080)
* R2RDump - dump generic method instances
* Added comments
* Move read functions to own class, changes to NativeHashtable to make it similar to NativeFormatReader
* Get type name for struct generic instances
* Emit # of runtimeFunctions and size:unavailable, add [Flags] to enum and NONE flag
* Throw exception when method entrypoint id is out of bounds
* Type name instead of var
* Get full classname including namespace
* Get parent types of nested types
* Save DeclaringType as string, rename variables, use 1 constructor
* Check generic param indices not out of bounds
Diffstat (limited to 'src')
-rw-r--r-- | src/tools/r2rdump/NativeArray.cs | 70 | ||||
-rw-r--r-- | src/tools/r2rdump/NativeHashtable.cs | 181 | ||||
-rw-r--r-- | src/tools/r2rdump/NativeReader.cs | 204 | ||||
-rw-r--r-- | src/tools/r2rdump/R2RDump.cs | 4 | ||||
-rw-r--r-- | src/tools/r2rdump/R2RHeader.cs | 18 | ||||
-rw-r--r-- | src/tools/r2rdump/R2RMethod.cs | 295 | ||||
-rw-r--r-- | src/tools/r2rdump/R2RReader.cs | 163 | ||||
-rw-r--r-- | src/tools/r2rdump/SignatureType.cs | 118 |
8 files changed, 786 insertions, 267 deletions
diff --git a/src/tools/r2rdump/NativeArray.cs b/src/tools/r2rdump/NativeArray.cs index ccaad808d3..41042ff54c 100644 --- a/src/tools/r2rdump/NativeArray.cs +++ b/src/tools/r2rdump/NativeArray.cs @@ -14,7 +14,7 @@ namespace R2RDump public NativeArray(byte[] image, uint offset) { uint val = 0; - _baseOffset = DecodeUnsigned(image, offset, ref val); + _baseOffset = NativeReader.DecodeUnsigned(image, offset, ref val); _nElements = (val >> 2); _entryIndexSize = (byte)(val & 3); } @@ -24,7 +24,7 @@ namespace R2RDump return _nElements; } - public bool TryGetAt(byte[] image, uint index, ref uint pOffset) + public bool TryGetAt(byte[] image, uint index, ref int pOffset) { if (index >= _nElements) return false; @@ -33,24 +33,24 @@ namespace R2RDump if (_entryIndexSize == 0) { int i = (int)(_baseOffset + (index / _blockSize)); - offset = R2RReader.ReadByte(image, ref i); + offset = NativeReader.ReadByte(image, ref i); } else if (_entryIndexSize == 1) { int i = (int)(_baseOffset + 2 * (index / _blockSize)); - offset = R2RReader.ReadUInt16(image, ref i); + offset = NativeReader.ReadUInt16(image, ref i); } else { int i = (int)(_baseOffset + 4 * (index / _blockSize)); - offset = R2RReader.ReadUInt32(image, ref i); + offset = NativeReader.ReadUInt32(image, ref i); } offset += _baseOffset; for (uint bit = _blockSize >> 1; bit > 0; bit >>= 1) { uint val = 0; - uint offset2 = DecodeUnsigned(image, offset, ref val); + uint offset2 = NativeReader.DecodeUnsigned(image, offset, ref val); if ((index & bit) != 0) { if ((val & 2) != 0) @@ -80,64 +80,8 @@ namespace R2RDump } return false; } - pOffset = offset; + pOffset = (int)offset; return true; } - - public uint DecodeUnsigned(byte[] image, uint offset, ref uint pValue) - { - if (offset >= image.Length) - throw new System.BadImageFormatException("NativeArray offset out of bounds"); - - int off = (int)offset; - uint val = R2RReader.ReadByte(image, ref off); - - if ((val & 1) == 0) - { - pValue = (val >> 1); - offset += 1; - } - else if ((val & 2) == 0) - { - if (offset + 1 >= image.Length) - throw new System.BadImageFormatException("NativeArray offset out of bounds"); - - pValue = (val >> 2) | - ((uint)R2RReader.ReadByte(image, ref off) << 6); - offset += 2; - } - else if ((val & 4) == 0) - { - if (offset + 2 >= image.Length) - throw new System.BadImageFormatException("NativeArray offset out of bounds"); - - pValue = (val >> 3) | - ((uint)R2RReader.ReadByte(image, ref off) << 5) | - ((uint)R2RReader.ReadByte(image, ref off) << 13); - offset += 3; - } - else if ((val & 8) == 0) - { - if (offset + 3 >= image.Length) - throw new System.BadImageFormatException("NativeArray offset out of bounds"); - - pValue = (val >> 4) | - ((uint)R2RReader.ReadByte(image, ref off) << 4) | - ((uint)R2RReader.ReadByte(image, ref off) << 12) | - ((uint)R2RReader.ReadByte(image, ref off) << 20); - offset += 4; - } - else if ((val & 16) == 0) - { - pValue = R2RReader.ReadUInt32(image, ref off); - offset += 5; - } - else - { - throw new System.BadImageFormatException("NativeArray"); - } - - return offset; - } } } diff --git a/src/tools/r2rdump/NativeHashtable.cs b/src/tools/r2rdump/NativeHashtable.cs new file mode 100644 index 0000000000..902080017b --- /dev/null +++ b/src/tools/r2rdump/NativeHashtable.cs @@ -0,0 +1,181 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace R2RDump +{ + struct NativeParser + { + /// <summary> + /// The current index of the image byte array + /// </summary> + public uint Offset { get; set; } + + byte[] _image; + + public NativeParser(byte[] image, uint offset) + { + Offset = offset; + _image = image; + } + + public bool IsNull() + { + return _image == null; + } + + public uint GetRelativeOffset() + { + uint pos = Offset; + + int delta = 0; + Offset = NativeReader.DecodeSigned(_image, Offset, ref delta); + + return pos + (uint)delta; + } + + public NativeParser GetParserFromRelativeOffset() + { + return new NativeParser(_image, GetRelativeOffset()); + } + + public byte GetByte() + { + int off = (int)Offset; + byte val = NativeReader.ReadByte(_image, ref off); + Offset += 1; + return val; + } + + public uint GetUnsigned() + { + uint value = 0; + Offset = NativeReader.DecodeUnsigned(_image, Offset, ref value); + return value; + } + + public int GetSigned() + { + int value = 0; + Offset = NativeReader.DecodeSigned(_image, Offset, ref value); + return value; + } + } + + struct NativeHashtable + { + private byte[] _image; + private uint _baseOffset; + private uint _bucketMask; + private byte _entryIndexSize; + + public NativeHashtable(byte[] image, NativeParser parser) + { + uint header = parser.GetByte(); + _baseOffset = parser.Offset; + _image = image; + + int numberOfBucketsShift = (int)(header >> 2); + if (numberOfBucketsShift > 31) + throw new System.BadImageFormatException(); + _bucketMask = (uint)((1 << numberOfBucketsShift) - 1); + + byte entryIndexSize = (byte)(header & 3); + if (entryIndexSize > 2) + throw new System.BadImageFormatException(); + _entryIndexSize = entryIndexSize; + } + + // + // The enumerator does not conform to the regular C# enumerator pattern to avoid paying + // its performance penalty (allocation, multiple calls per iteration) + // + public struct Enumerator + { + private NativeParser _parser; + private uint _endOffset; + private byte _lowHashcode; + + internal Enumerator(NativeParser parser, uint endOffset, byte lowHashcode) + { + _parser = parser; + _endOffset = endOffset; + _lowHashcode = lowHashcode; + } + } + + public struct AllEntriesEnumerator + { + private NativeHashtable _table; + private NativeParser _parser; + private uint _currentBucket; + private uint _endOffset; + + internal AllEntriesEnumerator(NativeHashtable table) + { + _table = table; + _currentBucket = 0; + _parser = _table.GetParserForBucket(_currentBucket, out _endOffset); + } + + public NativeParser GetNext() + { + for (; ; ) + { + while (_parser.Offset < _endOffset) + { + byte lowHashcode = _parser.GetByte(); + return _parser.GetParserFromRelativeOffset(); + } + + if (_currentBucket >= _table._bucketMask) + return new NativeParser(); + + _currentBucket++; + _parser = _table.GetParserForBucket(_currentBucket, out _endOffset); + } + } + } + + private NativeParser GetParserForBucket(uint bucket, out uint endOffset) + { + uint start, end; + + if (_entryIndexSize == 0) + { + int bucketOffset = (int)(_baseOffset + bucket); + start = NativeReader.ReadByte(_image, ref bucketOffset); + end = NativeReader.ReadByte(_image, ref bucketOffset); + } + else if (_entryIndexSize == 1) + { + int bucketOffset = (int)(_baseOffset + 2 * bucket); + start = NativeReader.ReadUInt16(_image, ref bucketOffset); + end = NativeReader.ReadUInt16(_image, ref bucketOffset); + } + else + { + int bucketOffset = (int)(_baseOffset + 4 * bucket); + start = NativeReader.ReadUInt32(_image, ref bucketOffset); + end = NativeReader.ReadUInt32(_image, ref bucketOffset); + } + + endOffset = end + _baseOffset; + return new NativeParser(_image, _baseOffset + start); + } + + public Enumerator Lookup(int hashcode) + { + uint endOffset; + uint bucket = ((uint)hashcode >> 8) & _bucketMask; + NativeParser parser = GetParserForBucket(bucket, out endOffset); + + return new Enumerator(parser, endOffset, (byte)hashcode); + } + + public AllEntriesEnumerator EnumerateAllEntries() + { + return new AllEntriesEnumerator(this); + } + } +} diff --git a/src/tools/r2rdump/NativeReader.cs b/src/tools/r2rdump/NativeReader.cs new file mode 100644 index 0000000000..13b674433f --- /dev/null +++ b/src/tools/r2rdump/NativeReader.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace R2RDump +{ + class NativeReader + { + /// <summary> + /// Extracts a 64bit value from the image byte array + /// </summary> + /// <param name="image">PE image</param> + /// <param name="start">Starting index of the value</param> + /// <remarks> + /// The <paramref name="start"/> gets incremented by the size of the value + /// </remarks> + public static long ReadInt64(byte[] image, ref int start) + { + int size = sizeof(long); + byte[] bytes = new byte[size]; + Array.Copy(image, start, bytes, 0, size); + start += size; + return BitConverter.ToInt64(bytes, 0); + } + + // <summary> + /// Extracts a 32bit value from the image byte array + /// </summary> + /// <param name="image">PE image</param> + /// <param name="start">Starting index of the value</param> + /// <remarks> + /// The <paramref name="start"/> gets incremented by the size of the value + /// </remarks> + public static int ReadInt32(byte[] image, ref int start) + { + int size = sizeof(int); + byte[] bytes = new byte[size]; + Array.Copy(image, start, bytes, 0, size); + start += size; + return BitConverter.ToInt32(bytes, 0); + } + + // <summary> + /// Extracts an unsigned 32bit value from the image byte array + /// </summary> + /// <param name="image">PE image</param> + /// <param name="start">Starting index of the value</param> + /// <remarks> + /// The <paramref name="start"/> gets incremented by the size of the value + /// </remarks> + public static uint ReadUInt32(byte[] image, ref int start) + { + int size = sizeof(int); + byte[] bytes = new byte[size]; + Array.Copy(image, start, bytes, 0, size); + start += size; + return (uint)BitConverter.ToInt32(bytes, 0); + } + + // <summary> + /// Extracts an unsigned 16bit value from the image byte array + /// </summary> + /// <param name="image">PE image</param> + /// <param name="start">Starting index of the value</param> + /// <remarks> + /// The <paramref name="start"/> gets incremented by the size of the value + /// </remarks> + public static ushort ReadUInt16(byte[] image, ref int start) + { + int size = sizeof(short); + byte[] bytes = new byte[size]; + Array.Copy(image, start, bytes, 0, size); + start += size; + return (ushort)BitConverter.ToInt16(bytes, 0); + } + + // <summary> + /// Extracts byte from the image byte array + /// </summary> + /// <param name="image">PE image</param> + /// <param name="start">Start index of the value</param> + /// /// <remarks> + /// The <paramref name="start"/> gets incremented by the size of the value + /// </remarks> + public static byte ReadByte(byte[] image, ref int start) + { + byte val = image[start]; + start += sizeof(byte); + return val; + } + + public static uint DecodeUnsigned(byte[] image, uint offset, ref uint pValue) + { + if (offset >= image.Length) + throw new System.BadImageFormatException("offset out of bounds"); + + int off = (int)offset; + uint val = ReadByte(image, ref off); + + if ((val & 1) == 0) + { + pValue = (val >> 1); + offset += 1; + } + else if ((val & 2) == 0) + { + if (offset + 1 >= image.Length) + throw new System.BadImageFormatException("offset out of bounds"); + + pValue = (val >> 2) | + ((uint)ReadByte(image, ref off) << 6); + offset += 2; + } + else if ((val & 4) == 0) + { + if (offset + 2 >= image.Length) + throw new System.BadImageFormatException("offset out of bounds"); + + pValue = (val >> 3) | + ((uint)ReadByte(image, ref off) << 5) | + ((uint)ReadByte(image, ref off) << 13); + offset += 3; + } + else if ((val & 8) == 0) + { + if (offset + 3 >= image.Length) + throw new System.BadImageFormatException("offset out of bounds"); + + pValue = (val >> 4) | + ((uint)ReadByte(image, ref off) << 4) | + ((uint)ReadByte(image, ref off) << 12) | + ((uint)ReadByte(image, ref off) << 20); + offset += 4; + } + else if ((val & 16) == 0) + { + pValue = ReadUInt32(image, ref off); + offset += 5; + } + else + { + throw new System.BadImageFormatException("DecodeUnsigned"); + } + + return offset; + } + + public static uint DecodeSigned(byte[] image, uint offset, ref int pValue) + { + if (offset >= image.Length) + throw new System.BadImageFormatException("offset out of bounds"); + + int off = (int)offset; + int val = ReadByte(image, ref off); + + if ((val & 1) == 0) + { + pValue = (val >> 1); + offset += 1; + } + else if ((val & 2) == 0) + { + if (offset + 1 >= image.Length) + throw new System.BadImageFormatException("offset out of bounds"); + + pValue = (val >> 2) | + (ReadByte(image, ref off) << 6); + offset += 2; + } + else if ((val & 4) == 0) + { + if (offset + 2 >= image.Length) + throw new System.BadImageFormatException("offset out of bounds"); + + pValue = (val >> 3) | + (ReadByte(image, ref off) << 5) | + (ReadByte(image, ref off) << 13); + offset += 3; + } + else if ((val & 8) == 0) + { + if (offset + 3 >= image.Length) + throw new System.BadImageFormatException("offset out of bounds"); + + pValue = (val >> 4) | + (ReadByte(image, ref off) << 4) | + (ReadByte(image, ref off) << 12) | + (ReadByte(image, ref off) << 20); + offset += 4; + } + else if ((val & 16) == 0) + { + pValue = ReadInt32(image, ref off); + offset += 5; + } + else + { + throw new System.BadImageFormatException("DecodeSigned"); + } + + return offset; + } + } +} diff --git a/src/tools/r2rdump/R2RDump.cs b/src/tools/r2rdump/R2RDump.cs index a54d7291af..7d0bd2fd19 100644 --- a/src/tools/r2rdump/R2RDump.cs +++ b/src/tools/r2rdump/R2RDump.cs @@ -39,10 +39,10 @@ namespace R2RDump Console.WriteLine(section.Value.ToString()); } - Console.WriteLine("============== Native Code =============="); + Console.WriteLine("============== Native Code ==============\n"); foreach (R2RMethod method in r2r.R2RMethods) { - Console.WriteLine(method.ToString()); + Console.Write(method.ToString()); Console.WriteLine("------------------\n"); } } diff --git a/src/tools/r2rdump/R2RHeader.cs b/src/tools/r2rdump/R2RHeader.cs index 1441833f35..33ef42431a 100644 --- a/src/tools/r2rdump/R2RHeader.cs +++ b/src/tools/r2rdump/R2RHeader.cs @@ -10,8 +10,10 @@ namespace R2RDump { class R2RHeader { + [Flags] public enum ReadyToRunFlag { + NONE = 0x00000000, READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE = 0x00000001, READYTORUN_FLAG_SKIP_TYPE_VALIDATION = 0x00000002 } @@ -69,29 +71,29 @@ namespace R2RDump byte[] signature = new byte[sizeof(uint)]; Array.Copy(image, curOffset, signature, 0, sizeof(uint)); SignatureString = System.Text.Encoding.UTF8.GetString(signature); - Signature = R2RReader.ReadUInt32(image, ref curOffset); + Signature = NativeReader.ReadUInt32(image, ref curOffset); if (Signature != READYTORUN_SIGNATURE) { throw new System.BadImageFormatException("Incorrect R2R header signature"); } - MajorVersion = R2RReader.ReadUInt16(image, ref curOffset); - MinorVersion = R2RReader.ReadUInt16(image, ref curOffset); - Flags = R2RReader.ReadUInt32(image, ref curOffset); - int nSections = R2RReader.ReadInt32(image, ref curOffset); + MajorVersion = NativeReader.ReadUInt16(image, ref curOffset); + MinorVersion = NativeReader.ReadUInt16(image, ref curOffset); + Flags = NativeReader.ReadUInt32(image, ref curOffset); + int nSections = NativeReader.ReadInt32(image, ref curOffset); Sections = new Dictionary<R2RSection.SectionType, R2RSection>(); for (int i = 0; i < nSections; i++) { - int type = R2RReader.ReadInt32(image, ref curOffset); + int type = NativeReader.ReadInt32(image, ref curOffset); var sectionType = (R2RSection.SectionType)type; if (!Enum.IsDefined(typeof(R2RSection.SectionType), type)) { R2RDump.OutputWarning("Invalid ReadyToRun section type"); } Sections[sectionType] = new R2RSection(sectionType, - R2RReader.ReadInt32(image, ref curOffset), - R2RReader.ReadInt32(image, ref curOffset)); + NativeReader.ReadInt32(image, ref curOffset), + NativeReader.ReadInt32(image, ref curOffset)); } Size = curOffset - startOffset; diff --git a/src/tools/r2rdump/R2RMethod.cs b/src/tools/r2rdump/R2RMethod.cs index a709c74f02..fd077b6b7c 100644 --- a/src/tools/r2rdump/R2RMethod.cs +++ b/src/tools/r2rdump/R2RMethod.cs @@ -2,77 +2,26 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; +using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Text; namespace R2RDump { - struct SignatureType + struct RuntimeFunction { /// <summary> - /// The SignatureTypeCode can be a primitive type, TypeHandle for objects, ByReference for references - /// </summary> - public SignatureTypeCode SignatureTypeCode { get; } - - /// <summary> - /// Indicates if the type is an array - /// </summary> - public bool IsArray { get; } - - /// <summary> - /// Name of the object or primitive type + /// The index of the runtime function /// </summary> - public string ClassName { get; } - - public SignatureType(ref BlobReader signatureReader, ref MetadataReader mdReader) - { - SignatureTypeCode = signatureReader.ReadSignatureTypeCode(); - IsArray = (SignatureTypeCode == SignatureTypeCode.SZArray); - if (IsArray) - { - SignatureTypeCode = signatureReader.ReadSignatureTypeCode(); - } - ClassName = SignatureTypeCode.ToString(); - if (SignatureTypeCode == SignatureTypeCode.TypeHandle || SignatureTypeCode == SignatureTypeCode.ByReference) - { - EntityHandle handle = signatureReader.ReadTypeHandle(); - if (handle.Kind == HandleKind.TypeDefinition) - { - var typeDef = mdReader.GetTypeDefinition((TypeDefinitionHandle)handle); - ClassName = mdReader.GetString(typeDef.Name); - } - else if (handle.Kind == HandleKind.TypeReference) - { - var typeRef = mdReader.GetTypeReference((TypeReferenceHandle)handle); - ClassName = mdReader.GetString(typeRef.Name); - } - } - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - if (SignatureTypeCode == SignatureTypeCode.ByReference) - { - sb.Append("ref "); - } - sb.AppendFormat($"{ClassName}"); - if (IsArray) - { - sb.Append("[]"); - } - return sb.ToString(); - } - } + public int Id { get; } - struct RuntimeFunction - { /// <summary> /// The relative virtual address to the start of the code block /// </summary> - public int StartAddress { get; set; } + public int StartAddress { get; } /// <summary> /// The size of the code block in bytes @@ -81,32 +30,56 @@ namespace R2RDump /// The EndAddress field in the runtime functions section is conditional on machine type /// Size is -1 for images without the EndAddress field /// </remarks> - public int Size { get; set; } + public int Size { get; } /// <summary> /// The relative virtual address to the unwind info /// </summary> - public int UnwindRVA { get; set; } + public int UnwindRVA { get; } - public RuntimeFunction(int startRva, int endRva, int unwindRva) + public RuntimeFunction(int id, int startRva, int endRva, int unwindRva) { + Id = id; StartAddress = startRva; Size = endRva - startRva; if (endRva == -1) Size = -1; UnwindRVA = unwindRva; } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat($"Id: {Id}\n"); + sb.AppendFormat($"StartAddress: 0x{StartAddress:X8}\n"); + if (Size == -1) + { + sb.Append("Size: Unavailable\n"); + } + else + { + sb.AppendFormat($"Size: {Size} bytes\n"); + } + + return sb.ToString(); + } } class R2RMethod { private const int _mdtMethodDef = 0x06000000; + MetadataReader _mdReader; + MethodDefinition _methodDef; + /// <summary> /// The name of the method /// </summary> public string Name { get; } + public bool IsGeneric { get; } + /// <summary> /// The return type of the method /// </summary> @@ -118,6 +91,11 @@ namespace R2RDump public SignatureType[] ArgTypes { get; } /// <summary> + /// The type that the method belongs to + /// </summary> + public string DeclaringType { get; } + + /// <summary> /// The token of the method consisting of the table code (0x06) and row id /// </summary> public uint Token { get; } @@ -125,57 +103,144 @@ namespace R2RDump /// <summary> /// All the runtime functions of this method /// </summary> - public List<RuntimeFunction> NativeCode { get; } + public List<RuntimeFunction> RuntimeFunctions { get; } /// <summary> /// The id of the entrypoint runtime function /// </summary> - public uint EntryPointRuntimeFunctionId { get; } + public int EntryPointRuntimeFunctionId { get; } + + /// <summary> + /// Maps all the generic parameters to the type in the instance + /// </summary> + Dictionary<string, GenericInstance> _genericParamInstanceMap; + + [Flags] + public enum EncodeMethodSigFlags + { + NONE = 0x00, + ENCODE_METHOD_SIG_UnboxingStub = 0x01, + ENCODE_METHOD_SIG_InstantiatingStub = 0x02, + ENCODE_METHOD_SIG_MethodInstantiation = 0x04, + ENCODE_METHOD_SIG_SlotInsteadOfToken = 0x08, + ENCODE_METHOD_SIG_MemberRefToken = 0x10, + ENCODE_METHOD_SIG_Constrained = 0x20, + ENCODE_METHOD_SIG_OwnerType = 0x40, + }; - public R2RMethod(byte[] image, MetadataReader mdReader, NativeArray methodEntryPoints, uint offset, uint rid) + public enum GenericElementTypes + { + __Canon = 0x3e, + Void = 0x01, + Boolean = 0x02, + Char = 0x03, + Int8 = 0x04, + UInt8 = 0x05, + Int16 = 0x06, + UInt16 = 0x07, + Int32 = 0x08, + UInt32 = 0x09, + Int64 = 0x0a, + UInt64 = 0x0b, + Float = 0x0c, + Double = 0x0d, + String = 0x0e, + ValueType = 0x11, + Object = 0x1c, + Array = 0x1d, + }; + + /// <summary> + /// Extracts the method signature from the metadata by rid + /// </summary> + public R2RMethod(byte[] image, MetadataReader mdReader, uint rid, int entryPointId, GenericElementTypes[] instanceArgs, uint[] tok) { - // get the id of the entry point runtime function from the MethodEntryPoints NativeArray Token = _mdtMethodDef | rid; - uint id = 0; // the RUNTIME_FUNCTIONS index - offset = methodEntryPoints.DecodeUnsigned(image, offset, ref id); - if ((id & 1) != 0) + EntryPointRuntimeFunctionId = entryPointId; + + _mdReader = mdReader; + RuntimeFunctions = new List<RuntimeFunction>(); + + // get the method signature from the MethodDefhandle + MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)rid); + _methodDef = mdReader.GetMethodDefinition(methodDefHandle); + Name = mdReader.GetString(_methodDef.Name); + BlobReader signatureReader = mdReader.GetBlobReader(_methodDef.Signature); + + TypeDefinitionHandle declaringTypeHandle = _methodDef.GetDeclaringType(); + TypeDefinition declaringTypeDef; + while (!declaringTypeHandle.IsNil) { - if ((id & 2) != 0) - { - uint val = 0; - methodEntryPoints.DecodeUnsigned(image, offset, ref val); - offset -= val; - } - // TODO: Dump fixups + declaringTypeDef = mdReader.GetTypeDefinition(declaringTypeHandle); + DeclaringType = mdReader.GetString(declaringTypeDef.Name) + "." + DeclaringType; + declaringTypeHandle = declaringTypeDef.GetDeclaringType(); + } + + NamespaceDefinitionHandle namespaceHandle = declaringTypeDef.NamespaceDefinition; + while (!namespaceHandle.IsNil) + { + NamespaceDefinition namespaceDef = mdReader.GetNamespaceDefinition(namespaceHandle); + DeclaringType = mdReader.GetString(namespaceDef.Name) + "." + DeclaringType; + namespaceHandle = namespaceDef.Parent; + } - id >>= 2; + SignatureHeader signatureHeader = signatureReader.ReadSignatureHeader(); + IsGeneric = signatureHeader.IsGeneric; + GenericParameterHandleCollection genericParams = _methodDef.GetGenericParameters(); + _genericParamInstanceMap = new Dictionary<string, GenericInstance>(); + + int argCount = signatureReader.ReadCompressedInteger(); + if (IsGeneric) + { + argCount = signatureReader.ReadCompressedInteger(); } - else + + ReturnType = new SignatureType(ref signatureReader, mdReader, genericParams); + ArgTypes = new SignatureType[argCount]; + for (int i = 0; i < argCount; i++) { - id >>= 1; + ArgTypes[i] = new SignatureType(ref signatureReader, mdReader, genericParams); } - EntryPointRuntimeFunctionId = id; - NativeCode = new List<RuntimeFunction>(); - // get the method signature from the MethodDefhandle - try + if (IsGeneric && instanceArgs != null && tok != null) { - MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)rid); - var methodDef = mdReader.GetMethodDefinition(methodDefHandle); - BlobReader signatureReader = mdReader.GetBlobReader(methodDef.Signature); - SignatureHeader header = signatureReader.ReadSignatureHeader(); - Name = mdReader.GetString(methodDef.Name); - int argCount = signatureReader.ReadCompressedInteger(); - ReturnType = new SignatureType(ref signatureReader, ref mdReader); - ArgTypes = new SignatureType[argCount]; - for (int i = 0; i < argCount; i++) + InitGenericInstances(genericParams, instanceArgs, tok); + } + } + + private void InitGenericInstances(GenericParameterHandleCollection genericParams, GenericElementTypes[] instanceArgs, uint[] tok) + { + if (instanceArgs.Length != genericParams.Count || tok.Length != genericParams.Count) + { + throw new BadImageFormatException("Generic param indices out of bounds"); + } + + for (int i = 0; i < genericParams.Count; i++) + { + var key = _mdReader.GetString(_mdReader.GetGenericParameter(genericParams.ElementAt(i)).Name); + + if (instanceArgs[i] == GenericElementTypes.ValueType) + { + string classname = _mdReader.GetString(_mdReader.GetTypeDefinition(MetadataTokens.TypeDefinitionHandle((int)tok[i])).Name); + _genericParamInstanceMap[key] = new GenericInstance(instanceArgs[i], classname); + } + else { - ArgTypes[i] = new SignatureType(ref signatureReader, ref mdReader); + _genericParamInstanceMap[key] = new GenericInstance(instanceArgs[i], Enum.GetName(typeof(GenericElementTypes), instanceArgs[i])); } } - catch (System.BadImageFormatException) + + if ((ReturnType.Flags & SignatureType.SignatureTypeFlags.GENERIC) != 0) { - R2RDump.OutputWarning("The method with rowId " + rid + " doesn't have a corresponding MethodDefHandle"); + ReturnType.GenericInstance = _genericParamInstanceMap[ReturnType.TypeName]; + } + + for (int i = 0; i < ArgTypes.Length; i++) + { + if ((ArgTypes[i].Flags & SignatureType.SignatureTypeFlags.GENERIC) != 0) + { + ArgTypes[i].GenericInstance = _genericParamInstanceMap[ArgTypes[i].TypeName]; + } } } @@ -183,24 +248,46 @@ namespace R2RDump { StringBuilder sb = new StringBuilder(); - if (Name != null) { - sb.AppendFormat($"{ReturnType.ToString()} {Name}("); - for (int i = 0; i < ArgTypes.Length - 1; i++) + if (Name != null) + { + sb.AppendFormat($"{ReturnType.ToString()} "); + sb.AppendFormat($"{DeclaringType}{Name}"); + + if (IsGeneric) { - sb.AppendFormat($"{ArgTypes[i].ToString()}, "); + sb.Append("<"); + int i = 0; + foreach (var instance in _genericParamInstanceMap.Values) + { + if (i > 0) + { + sb.Append(", "); + } + sb.AppendFormat($"{instance.TypeName}"); + i++; + } + sb.Append(">"); } - if (ArgTypes.Length > 0) { - sb.AppendFormat($"{ArgTypes[ArgTypes.Length - 1].ToString()}"); + + sb.Append("("); + for (int i = 0; i < ArgTypes.Length; i++) + { + if (i > 0) + { + sb.Append(", "); + } + sb.AppendFormat($"{ArgTypes[i].ToString()}"); } sb.Append(")\n"); } sb.AppendFormat($"Token: 0x{Token:X8}\n"); - foreach (RuntimeFunction runtimeFunction in NativeCode) { - sb.AppendFormat($"\nStartAddress: 0x{runtimeFunction.StartAddress:X8}\n"); - if (runtimeFunction.Size != -1) { - sb.AppendFormat($"Size: {runtimeFunction.Size} bytes\n"); - } + sb.AppendFormat($"EntryPointRuntimeFunctionId: {EntryPointRuntimeFunctionId}\n"); + sb.AppendFormat($"Number of RuntimeFunctions: {RuntimeFunctions.Count}\n\n"); + + foreach (RuntimeFunction runtimeFunction in RuntimeFunctions) + { + sb.AppendFormat($"{runtimeFunction}\n"); } return sb.ToString(); diff --git a/src/tools/r2rdump/R2RReader.cs b/src/tools/r2rdump/R2RReader.cs index a8045f86b2..343cf27344 100644 --- a/src/tools/r2rdump/R2RReader.cs +++ b/src/tools/r2rdump/R2RReader.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; namespace R2RDump @@ -88,7 +87,7 @@ namespace R2RDump // initialize R2RMethods if (peReader.HasMetadata) { - var mdReader = peReader.GetMetadataReader(); + MetadataReader mdReader = peReader.GetMetadataReader(); int runtimeFunctionSize = 2; if (Machine == Machine.Amd64) @@ -113,35 +112,77 @@ namespace R2RDump R2RMethods = new List<R2RMethod>(); for (uint rid = 1; rid <= nMethodEntryPoints; rid++) { - uint offset = 0; + int offset = 0; if (methodEntryPoints.TryGetAt(_image, rid - 1, ref offset)) { - R2RMethod method = new R2RMethod(_image, mdReader, methodEntryPoints, offset, rid); - if (method.EntryPointRuntimeFunctionId >= nRuntimeFunctions) + R2RMethod method = new R2RMethod(_image, mdReader, rid, GetEntryPointIdFromOffset(offset), null, null); + + if (method.EntryPointRuntimeFunctionId < 0 || method.EntryPointRuntimeFunctionId >= nRuntimeFunctions) { - throw new BadImageFormatException("Runtime function id out of bounds"); + throw new BadImageFormatException("EntryPointRuntimeFunctionId out of bounds"); } isEntryPoint[method.EntryPointRuntimeFunctionId] = true; R2RMethods.Add(method); } } + // instance method table + R2RSection instMethodEntryPointSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS]; + int instMethodEntryPointsOffset = GetOffset(instMethodEntryPointSection.RelativeVirtualAddress); + NativeParser parser = new NativeParser(_image, (uint)instMethodEntryPointsOffset); + NativeHashtable instMethodEntryPoints = new NativeHashtable(_image, parser); + NativeHashtable.AllEntriesEnumerator allEntriesEnum = instMethodEntryPoints.EnumerateAllEntries(); + NativeParser curParser = allEntriesEnum.GetNext(); + while (!curParser.IsNull()) + { + byte methodFlags = curParser.GetByte(); + byte rid = curParser.GetByte(); + if ((methodFlags & (byte)R2RMethod.EncodeMethodSigFlags.ENCODE_METHOD_SIG_MethodInstantiation) != 0) + { + byte nArgs = curParser.GetByte(); + R2RMethod.GenericElementTypes[] args = new R2RMethod.GenericElementTypes[nArgs]; + uint[] tokens = new uint[nArgs]; + for (int i = 0; i < nArgs; i++) + { + args[i] = (R2RMethod.GenericElementTypes)curParser.GetByte(); + if (args[i] == R2RMethod.GenericElementTypes.ValueType) + { + tokens[i] = curParser.GetByte(); + tokens[i] = (tokens[i] >> 2); + } + } + + uint id = curParser.GetUnsigned(); + id = id >> 1; + R2RMethod method = new R2RMethod(_image, mdReader, rid, (int)id, args, tokens); + if (method.EntryPointRuntimeFunctionId >= 0 && method.EntryPointRuntimeFunctionId < nRuntimeFunctions) + { + isEntryPoint[method.EntryPointRuntimeFunctionId] = true; + } + R2RMethods.Add(method); + } + curParser = allEntriesEnum.GetNext(); + } + // get the RVAs of the runtime functions for each method - int runtimeFunctionId = 0; + int curOffset = 0; foreach (R2RMethod method in R2RMethods) { - int curOffset = (int)(runtimeFunctionOffset + method.EntryPointRuntimeFunctionId * runtimeFunctionSize); + int runtimeFunctionId = method.EntryPointRuntimeFunctionId; + if (runtimeFunctionId == -1) + continue; + curOffset = runtimeFunctionOffset + runtimeFunctionId * runtimeFunctionSize; do { - int startRva = ReadInt32(_image, ref curOffset); + int startRva = NativeReader.ReadInt32(_image, ref curOffset); int endRva = -1; if (Machine == Machine.Amd64) { - endRva = ReadInt32(_image, ref curOffset); + endRva = NativeReader.ReadInt32(_image, ref curOffset); } - int unwindRva = ReadInt32(_image, ref curOffset); + int unwindRva = NativeReader.ReadInt32(_image, ref curOffset); - method.NativeCode.Add(new RuntimeFunction(startRva, endRva, unwindRva)); + method.RuntimeFunctions.Add(new RuntimeFunction(runtimeFunctionId, startRva, endRva, unwindRva)); runtimeFunctionId++; } while (runtimeFunctionId < nRuntimeFunctions && !isEntryPoint[runtimeFunctionId]); @@ -161,87 +202,29 @@ namespace R2RDump return rva - containingSection.VirtualAddress + containingSection.PointerToRawData; } - /// <summary> - /// Extracts a 64bit value from the image byte array - /// </summary> - /// <param name="image">PE image</param> - /// <param name="start">Starting index of the value</param> - /// <remarks> - /// The <paramref name="start"/> gets incremented by the size of the value - /// </remarks> - public static long ReadInt64(byte[] image, ref int start) - { - int size = sizeof(long); - byte[] bytes = new byte[size]; - Array.Copy(image, start, bytes, 0, size); - start += size; - return BitConverter.ToInt64(bytes, 0); - } - - // <summary> - /// Extracts a 32bit value from the image byte array - /// </summary> - /// <param name="image">PE image</param> - /// <param name="start">Starting index of the value</param> - /// <remarks> - /// The <paramref name="start"/> gets incremented by the size of the value - /// </remarks> - public static int ReadInt32(byte[] image, ref int start) - { - int size = sizeof(int); - byte[] bytes = new byte[size]; - Array.Copy(image, start, bytes, 0, size); - start += size; - return BitConverter.ToInt32(bytes, 0); - } - - // <summary> - /// Extracts an unsigned 32bit value from the image byte array - /// </summary> - /// <param name="image">PE image</param> - /// <param name="start">Starting index of the value</param> - /// <remarks> - /// The <paramref name="start"/> gets incremented by the size of the value - /// </remarks> - public static uint ReadUInt32(byte[] image, ref int start) + private int GetEntryPointIdFromOffset(int offset) { - int size = sizeof(int); - byte[] bytes = new byte[size]; - Array.Copy(image, start, bytes, 0, size); - start += size; - return (uint)BitConverter.ToInt32(bytes, 0); - } + // get the id of the entry point runtime function from the MethodEntryPoints NativeArray + uint id = 0; // the RUNTIME_FUNCTIONS index + offset = (int)NativeReader.DecodeUnsigned(_image, (uint)offset, ref id); + if ((id & 1) != 0) + { + if ((id & 2) != 0) + { + uint val = 0; + NativeReader.DecodeUnsigned(_image, (uint)offset, ref val); + offset -= (int)val; + } + // TODO: Dump fixups - // <summary> - /// Extracts an unsigned 16bit value from the image byte array - /// </summary> - /// <param name="image">PE image</param> - /// <param name="start">Starting index of the value</param> - /// <remarks> - /// The <paramref name="start"/> gets incremented by the size of the value - /// </remarks> - public static ushort ReadUInt16(byte[] image, ref int start) - { - int size = sizeof(short); - byte[] bytes = new byte[size]; - Array.Copy(image, start, bytes, 0, size); - start += size; - return (ushort)BitConverter.ToInt16(bytes, 0); - } + id >>= 2; + } + else + { + id >>= 1; + } - // <summary> - /// Extracts byte from the image byte array - /// </summary> - /// <param name="image">PE image</param> - /// <param name="start">Start index of the value</param> - /// /// <remarks> - /// The <paramref name="start"/> gets incremented by the size of the value - /// </remarks> - public static byte ReadByte(byte[] image, ref int start) - { - byte val = image[start]; - start += sizeof(byte); - return val; + return (int)id; } } } diff --git a/src/tools/r2rdump/SignatureType.cs b/src/tools/r2rdump/SignatureType.cs new file mode 100644 index 0000000000..e5efcfb115 --- /dev/null +++ b/src/tools/r2rdump/SignatureType.cs @@ -0,0 +1,118 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Reflection.Metadata; +using System.Text; + +namespace R2RDump +{ + class SignatureType + { + /// <summary> + /// Indicates if the type is an array, reference, or generic + /// </summary> + public SignatureTypeFlags Flags { get; } + + /// <summary> + /// Name of the object or primitive type, the placeholder type for generic methods + /// </summary> + public string TypeName { get; } + + /// <summary> + /// The type that the generic method was instantiated to + /// </summary> + public GenericInstance GenericInstance { get; set; } + + [Flags] + public enum SignatureTypeFlags + { + NONE = 0x00, + ARRAY = 0x01, + REFERENCE = 0x02, + GENERIC = 0x04, + }; + + public SignatureType(ref BlobReader signatureReader, MetadataReader mdReader, GenericParameterHandleCollection genericParams) + { + SignatureTypeCode signatureTypeCode = signatureReader.ReadSignatureTypeCode(); + Flags = 0; + if (signatureTypeCode == SignatureTypeCode.SZArray) + { + Flags |= SignatureTypeFlags.ARRAY; + signatureTypeCode = signatureReader.ReadSignatureTypeCode(); + } + + TypeName = signatureTypeCode.ToString(); + if (signatureTypeCode == SignatureTypeCode.TypeHandle || signatureTypeCode == SignatureTypeCode.ByReference) + { + if (signatureTypeCode == SignatureTypeCode.ByReference) + { + Flags |= SignatureTypeFlags.REFERENCE; + } + + EntityHandle handle = signatureReader.ReadTypeHandle(); + if (handle.Kind == HandleKind.TypeDefinition) + { + TypeDefinition typeDef = mdReader.GetTypeDefinition((TypeDefinitionHandle)handle); + TypeName = mdReader.GetString(typeDef.Name); + } + else if (handle.Kind == HandleKind.TypeReference) + { + TypeReference typeRef = mdReader.GetTypeReference((TypeReferenceHandle)handle); + TypeName = mdReader.GetString(typeRef.Name); + } + } + else if (signatureTypeCode == SignatureTypeCode.GenericMethodParameter) + { + int index = signatureReader.ReadCompressedInteger(); + GenericParameter generic = mdReader.GetGenericParameter(genericParams[index]); + TypeName = mdReader.GetString(generic.Name); + Flags |= SignatureTypeFlags.GENERIC; + } + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + if ((Flags & SignatureTypeFlags.REFERENCE) != 0) + { + sb.Append("ref "); + } + + if ((Flags & SignatureTypeFlags.GENERIC) != 0) + { + sb.AppendFormat($"{GenericInstance.TypeName}"); + } + else + { + sb.AppendFormat($"{TypeName}"); + } + if ((Flags & SignatureTypeFlags.ARRAY) != 0) + { + sb.Append("[]"); + } + return sb.ToString(); + } + } + + struct GenericInstance + { + /// <summary> + /// The type of the instance for generic a type + /// </summary> + public R2RMethod.GenericElementTypes Instance { get; } + + /// <summary> + /// The type name of the instance for generic a type. Different from GenericInstance.Instance for structs (ValueType) + /// </summary> + public string TypeName { get; } + + public GenericInstance(R2RMethod.GenericElementTypes instance, string name) + { + Instance = instance; + TypeName = name; + } + } +} |