summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoracmyu <amycmyu@gmail.com>2018-05-23 17:24:03 -0700
committerZach Montoya <zamont@microsoft.com>2018-05-23 17:24:03 -0700
commit6c934c21df759d4319e2006ef4add9e01068f648 (patch)
treea659e52bfda8b57c85523426aa3c066473cf0c71 /src
parentf4c6bebb1601596ea54baff489e50b64ad4a6a2e (diff)
downloadcoreclr-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.cs70
-rw-r--r--src/tools/r2rdump/NativeHashtable.cs181
-rw-r--r--src/tools/r2rdump/NativeReader.cs204
-rw-r--r--src/tools/r2rdump/R2RDump.cs4
-rw-r--r--src/tools/r2rdump/R2RHeader.cs18
-rw-r--r--src/tools/r2rdump/R2RMethod.cs295
-rw-r--r--src/tools/r2rdump/R2RReader.cs163
-rw-r--r--src/tools/r2rdump/SignatureType.cs118
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;
+ }
+ }
+}