summaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
authorTomáš Rylek <trylek@microsoft.com>2019-04-30 23:16:46 +0200
committerGitHub <noreply@github.com>2019-04-30 23:16:46 +0200
commitfc05a4297c903c1456762d89913d79e1780b55b1 (patch)
tree871f92528952366adc8652e686cbe9403e7090ae /src/tools
parentcdc344f7475e9e193b6bf7a454f5782b4d793be8 (diff)
downloadcoreclr-fc05a4297c903c1456762d89913d79e1780b55b1.tar.gz
coreclr-fc05a4297c903c1456762d89913d79e1780b55b1.tar.bz2
coreclr-fc05a4297c903c1456762d89913d79e1780b55b1.zip
Support for larger version bubbles in R2RDump (#24277)
After JanV fixed basically all test failures related to larger version bubbles originally implemented by Andon, it's high time to fix R2RDump for its support as a prerequisite to enabling larger version bubbles in CPAOT. This change implements this support via the following particular changes: 1) In DumpOptions, I added a list of explicit assembly references, another list of reference paths that are used for resolving assembly references, a cache for repeated assembly lookups and a new method FindAssembly for file resolution of a given simple assembly name. 2) I have moved ECMA metadata-related logic from R2RReader to its new base class EcmaMetadataReader. This class can represent both R2R and MSIL binaries and adds the logic for reference assembly lookup. 3) Large version bubble R2R PE exe's contain a special R2R header table READYTORUN_SECTION_MANIFEST_METADATA that contains ECMA metadata with a list of extra AssemblyRef's on top of those stored in the original MSIL. I have added support for parsing this table to the R2RReader constructor. 4) I have modified R2RSignature to be based off the new EcmaMetadataReader instead of R2RReader which cannot represent arbitrary reference assemblies. I have patched the two places dealing with module overrides to use the new logic to temporarily switch to a "remote module" SignatureDecoder for the purpose of parsing an external entity. 5) I have removed temporary hacks Andon put in place to keep R2RDump alive in some cases with larger version bubbles without proper support for parsing module overrides. 6) I have added logic for dumping the contents of the manifest metadata along with the standard AssemblyRef table to the header dump as I fought with recalculation of the indices when debugging larger version bubble code for quite some time. 7) As an extra half-forgotten bit from the past I added dumping of unboxing and instantiation stubs. I have noticed that the MSIL AssemblyRef count was off by one. I have also found out that XML output has bitrotten over time. This additional delta fixes both issues. [That forced me to add a bunch of empty ctors even though in practice we don't actually serialize anything into the dumper, we only use the XML serializer to emit the XML output file. Thanks Tomas
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/r2rdump/GCRefMap.cs4
-rw-r--r--src/tools/r2rdump/R2RDump.cs47
-rw-r--r--src/tools/r2rdump/R2RImportSection.cs4
-rw-r--r--src/tools/r2rdump/R2RReader.cs271
-rw-r--r--src/tools/r2rdump/R2RSection.cs3
-rw-r--r--src/tools/r2rdump/R2RSignature.cs124
-rw-r--r--src/tools/r2rdump/TextDumper.cs18
-rw-r--r--src/tools/r2rdump/XmlDumper.cs4
8 files changed, 343 insertions, 132 deletions
diff --git a/src/tools/r2rdump/GCRefMap.cs b/src/tools/r2rdump/GCRefMap.cs
index 55c06add61..ba3dc6ec93 100644
--- a/src/tools/r2rdump/GCRefMap.cs
+++ b/src/tools/r2rdump/GCRefMap.cs
@@ -41,6 +41,10 @@ namespace R2RDump
public readonly uint StackPop;
public readonly GCRefMapEntry[] Entries;
+ public GCRefMap()
+ {
+ }
+
public GCRefMap(uint stackPop, GCRefMapEntry[] entries)
{
StackPop = stackPop;
diff --git a/src/tools/r2rdump/R2RDump.cs b/src/tools/r2rdump/R2RDump.cs
index 07e329880b..cb5310b45e 100644
--- a/src/tools/r2rdump/R2RDump.cs
+++ b/src/tools/r2rdump/R2RDump.cs
@@ -26,6 +26,49 @@ namespace R2RDump
public bool GC;
public bool SectionContents;
public bool EntryPoints;
+
+ public IReadOnlyList<string> ReferenceAssemblies = Array.Empty<string>();
+ public IReadOnlyList<string> ReferencePaths = Array.Empty<string>();
+ public Dictionary<string, EcmaMetadataReader> AssemblyCache = new Dictionary<string, EcmaMetadataReader>(StringComparer.OrdinalIgnoreCase);
+
+ /// <summary>
+ /// Probing extensions to use when looking up assemblies under reference paths.
+ /// </summary>
+ private readonly static string[] ProbeExtensions = new string[] { ".ni.exe", ".ni.dll", ".exe", ".dll" };
+
+ /// <summary>
+ /// Try to locate a (reference) assembly using the list of explicit reference assemblies
+ /// and the list of reference paths passed to R2RDump.
+ /// </summary>
+ /// <param name="simpleName">Simple name of the assembly to look up</param>
+ /// <param name="parentFile">Name of assembly from which we're performing the lookup</param>
+ /// <returns></returns>
+ public string FindAssembly(string simpleName, string parentFile)
+ {
+ foreach (string refAsm in ReferenceAssemblies)
+ {
+ if (Path.GetFileNameWithoutExtension(refAsm).Equals(simpleName, StringComparison.OrdinalIgnoreCase))
+ {
+ return refAsm;
+ }
+ }
+
+ IEnumerable<string> allRefPaths = new string[] { Path.GetDirectoryName(parentFile) }.Concat(ReferencePaths);
+
+ foreach (string refPath in allRefPaths)
+ {
+ foreach (string extension in ProbeExtensions)
+ {
+ string probeFile = Path.Combine(refPath, simpleName + extension);
+ if (File.Exists(probeFile))
+ {
+ return probeFile;
+ }
+ }
+ }
+
+ return null;
+ }
}
public abstract class Dumper
@@ -131,7 +174,7 @@ namespace R2RDump
syntax.DefineOption("naked", ref _options.Naked, "Naked dump suppresses most compilation details like placement addresses");
syntax.DefineOptionList("q|query", ref _queries, "Query method by exact name, signature, row id or token");
syntax.DefineOptionList("k|keyword", ref _keywords, "Search method by keyword");
- syntax.DefineOptionList("r|runtimefunction", ref _runtimeFunctions, ArgStringToInt, "Get one runtime function by id or relative virtual address");
+ syntax.DefineOptionList("f|runtimefunction", ref _runtimeFunctions, ArgStringToInt, "Get one runtime function by id or relative virtual address");
syntax.DefineOptionList("s|section", ref _sections, "Get section by keyword");
syntax.DefineOption("unwind", ref _options.Unwind, "Dump unwindInfo");
syntax.DefineOption("gc", ref _options.GC, "Dump gcInfo and slot table");
@@ -141,6 +184,8 @@ namespace R2RDump
syntax.DefineOption("v|verbose", ref verbose, "Dump disassembly, unwindInfo, gcInfo and section contents");
syntax.DefineOption("diff", ref _diff, "Compare two R2R images");
syntax.DefineOption("ignoreSensitive", ref _ignoreSensitive, "Ignores sensitive properties in xml dump to avoid failing tests");
+ syntax.DefineOptionList("r|reference", ref _options.ReferenceAssemblies, "Explicit reference assembly files");
+ syntax.DefineOptionList("rp|referencepath", ref _options.ReferencePaths, "Search paths for reference assemblies");
});
if (verbose)
diff --git a/src/tools/r2rdump/R2RImportSection.cs b/src/tools/r2rdump/R2RImportSection.cs
index 8e5e32e322..b0ebcf0a4f 100644
--- a/src/tools/r2rdump/R2RImportSection.cs
+++ b/src/tools/r2rdump/R2RImportSection.cs
@@ -27,6 +27,10 @@ namespace R2RDump
public string Signature { get; set; }
public GCRefMap GCRefMap { get; set; }
+ public ImportSectionEntry()
+ {
+ }
+
public ImportSectionEntry(int index, int startOffset, int startRVA, long section, uint signatureRVA, string signature)
{
Index = index;
diff --git a/src/tools/r2rdump/R2RReader.cs b/src/tools/r2rdump/R2RReader.cs
index 62bf8a1f7b..2574f4d988 100644
--- a/src/tools/r2rdump/R2RReader.cs
+++ b/src/tools/r2rdump/R2RReader.cs
@@ -75,7 +75,7 @@ namespace R2RDump
}
}
- public class R2RReader
+ public class EcmaMetadataReader
{
/// <summary>
/// Option are used to specify details of signature formatting.
@@ -94,6 +94,18 @@ namespace R2RDump
public readonly MetadataReader MetadataReader;
/// <summary>
+ /// Extra reference assemblies parsed from the manifest metadata.
+ /// Only used by R2R assemblies with larger version bubble.
+ /// The manifest contains extra assembly references created by resolved
+ /// inlines and facades (non-existent in the source MSIL).
+ /// In module overrides, these assembly references are represented
+ /// by indices larger than the number of AssemblyRef rows in MetadataReader.
+ /// The list originates in the top-level R2R image and is copied
+ /// to all reference assemblies for the sake of simplicity.
+ /// </summary>
+ public readonly List<string> ManifestReferenceAssemblies;
+
+ /// <summary>
/// Byte array containing the ReadyToRun image
/// </summary>
public byte[] Image { get; }
@@ -104,6 +116,82 @@ namespace R2RDump
public string Filename { get; set; }
/// <summary>
+ /// The default constructor initializes an empty metadata reader.
+ /// </summary>
+ public EcmaMetadataReader()
+ {
+ }
+
+ /// <summary>
+ /// Open an MSIL binary and locate the metadata blob.
+ /// </summary>
+ /// <param name="options">Ambient options to use</param>
+ /// <param name="filename">PE image</param>
+ /// <param name="manifestReferenceAssemblies">List of reference assemblies from the R2R metadata manifest</param>
+ /// <exception cref="BadImageFormatException">The Cor header flag must be ILLibrary</exception>
+ public unsafe EcmaMetadataReader(DumpOptions options, string filename, List<string> manifestReferenceAssemblies)
+ {
+ Options = options;
+ Filename = filename;
+ ManifestReferenceAssemblies = manifestReferenceAssemblies;
+ Image = File.ReadAllBytes(filename);
+
+ fixed (byte* p = Image)
+ {
+ IntPtr ptr = (IntPtr)p;
+ PEReader = new PEReader(p, Image.Length);
+
+ if (!PEReader.HasMetadata)
+ {
+ throw new Exception($"ECMA metadata not found in file '{filename}'");
+ }
+
+ MetadataReader = PEReader.GetMetadataReader();
+ }
+ }
+
+ /// <summary>
+ /// Open a given reference assembly (relative to this ECMA metadata file).
+ /// </summary>
+ /// <param name="refAsmIndex">Reference assembly index</param>
+ /// <returns>EcmaMetadataReader instance representing the reference assembly</returns>
+ public EcmaMetadataReader OpenReferenceAssembly(int refAsmIndex)
+ {
+ if (refAsmIndex == 0)
+ {
+ return this;
+ }
+
+ int assemblyRefCount = MetadataReader.GetTableRowCount(TableIndex.AssemblyRef);
+ string name;
+ if (refAsmIndex <= assemblyRefCount)
+ {
+ AssemblyReference asmRef = MetadataReader.GetAssemblyReference(MetadataTokens.AssemblyReferenceHandle(refAsmIndex));
+ name = MetadataReader.GetString(asmRef.Name);
+ }
+ else
+ {
+ name = ManifestReferenceAssemblies[refAsmIndex - assemblyRefCount - 2];
+ }
+
+ EcmaMetadataReader ecmaReader;
+ if (!Options.AssemblyCache.TryGetValue(name, out ecmaReader))
+ {
+ string assemblyPath = Options.FindAssembly(name, Filename);
+ if (assemblyPath == null)
+ {
+ throw new Exception($"Missing reference assembly: {name}");
+ }
+ ecmaReader = new EcmaMetadataReader(Options, assemblyPath, ManifestReferenceAssemblies);
+ Options.AssemblyCache.Add(name, ecmaReader);
+ }
+ return ecmaReader;
+ }
+ }
+
+ public class R2RReader : EcmaMetadataReader
+ {
+ /// <summary>
/// True if the image is ReadyToRun
/// </summary>
public bool IsR2R { get; set; }
@@ -176,7 +264,7 @@ namespace R2RDump
private Dictionary<int, DebugInfo> _runtimeFunctionToDebugInfo = new Dictionary<int, DebugInfo>();
- public unsafe R2RReader() { }
+ public R2RReader() { }
/// <summary>
/// Initializes the fields of the R2RHeader and R2RMethods
@@ -184,118 +272,121 @@ namespace R2RDump
/// <param name="filename">PE image</param>
/// <exception cref="BadImageFormatException">The Cor header flag must be ILLibrary</exception>
public unsafe R2RReader(DumpOptions options, string filename)
+ : base(options, filename, new List<string>())
{
- Options = options;
- Filename = filename;
- Image = File.ReadAllBytes(filename);
-
- fixed (byte* p = Image)
+ IsR2R = ((PEReader.PEHeaders.CorHeader.Flags & CorFlags.ILLibrary) != 0);
+ if (!IsR2R)
{
- IntPtr ptr = (IntPtr)p;
- PEReader = new PEReader(p, Image.Length);
+ throw new BadImageFormatException("The file is not a ReadyToRun image");
+ }
- IsR2R = ((PEReader.PEHeaders.CorHeader.Flags & CorFlags.ILLibrary) != 0);
- if (!IsR2R)
+ uint machine = (uint)PEReader.PEHeaders.CoffHeader.Machine;
+ OS = OperatingSystem.Unknown;
+ foreach (OperatingSystem os in Enum.GetValues(typeof(OperatingSystem)))
+ {
+ Machine = (Machine)(machine ^ (uint)os);
+ if (Enum.IsDefined(typeof(Machine), Machine))
{
- throw new BadImageFormatException("The file is not a ReadyToRun image");
+ OS = os;
+ break;
}
+ }
+ if (OS == OperatingSystem.Unknown)
+ {
+ throw new BadImageFormatException($"Invalid Machine: {machine}");
+ }
- uint machine = (uint)PEReader.PEHeaders.CoffHeader.Machine;
- OS = OperatingSystem.Unknown;
- foreach(OperatingSystem os in Enum.GetValues(typeof(OperatingSystem)))
- {
- Machine = (Machine)(machine ^ (uint)os);
- if (Enum.IsDefined(typeof(Machine), Machine))
- {
- OS = os;
- break;
- }
- }
- if (OS == OperatingSystem.Unknown)
- {
- throw new BadImageFormatException($"Invalid Machine: {machine}");
- }
+ switch (Machine)
+ {
+ case Machine.I386:
+ Architecture = Architecture.X86;
+ PointerSize = 4;
+ break;
- switch (Machine)
- {
- case Machine.I386:
- Architecture = Architecture.X86;
- PointerSize = 4;
- break;
+ case Machine.Amd64:
+ Architecture = Architecture.X64;
+ PointerSize = 8;
+ break;
- case Machine.Amd64:
- Architecture = Architecture.X64;
- PointerSize = 8;
- break;
+ case Machine.Arm:
+ case Machine.Thumb:
+ case Machine.ArmThumb2:
+ Architecture = Architecture.Arm;
+ PointerSize = 4;
+ break;
- case Machine.Arm:
- case Machine.Thumb:
- case Machine.ArmThumb2:
- Architecture = Architecture.Arm;
- PointerSize = 4;
- break;
+ case Machine.Arm64:
+ Architecture = Architecture.Arm64;
+ PointerSize = 8;
+ break;
- case Machine.Arm64:
- Architecture = Architecture.Arm64;
- PointerSize = 8;
- break;
+ default:
+ throw new NotImplementedException(Machine.ToString());
+ }
- default:
- throw new NotImplementedException(Machine.ToString());
- }
+ ImageBase = PEReader.PEHeaders.PEHeader.ImageBase;
- ImageBase = PEReader.PEHeaders.PEHeader.ImageBase;
+ // initialize R2RHeader
+ DirectoryEntry r2rHeaderDirectory = PEReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory;
+ int r2rHeaderOffset = GetOffset(r2rHeaderDirectory.RelativeVirtualAddress);
+ R2RHeader = new R2RHeader(Image, r2rHeaderDirectory.RelativeVirtualAddress, r2rHeaderOffset);
+ if (r2rHeaderDirectory.Size != R2RHeader.Size)
+ {
+ throw new BadImageFormatException("The calculated size of the R2RHeader doesn't match the size saved in the ManagedNativeHeaderDirectory");
+ }
- // initialize R2RHeader
- DirectoryEntry r2rHeaderDirectory = PEReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory;
- int r2rHeaderOffset = GetOffset(r2rHeaderDirectory.RelativeVirtualAddress);
- R2RHeader = new R2RHeader(Image, r2rHeaderDirectory.RelativeVirtualAddress, r2rHeaderOffset);
- if (r2rHeaderDirectory.Size != R2RHeader.Size)
- {
- throw new BadImageFormatException("The calculated size of the R2RHeader doesn't match the size saved in the ManagedNativeHeaderDirectory");
- }
+ ParseDebugInfo();
- if (PEReader.HasMetadata)
+ if (R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_MANIFEST_METADATA))
+ {
+ R2RSection manifestMetadata = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_MANIFEST_METADATA];
+ fixed (byte* image = Image)
{
- MetadataReader = PEReader.GetMetadataReader();
-
- ParseDebugInfo();
-
- if (R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_EXCEPTION_INFO))
+ MetadataReader manifestReader = new MetadataReader(image + GetOffset(manifestMetadata.RelativeVirtualAddress), manifestMetadata.Size);
+ int assemblyRefCount = manifestReader.GetTableRowCount(TableIndex.AssemblyRef);
+ for (int assemblyRefIndex = 1; assemblyRefIndex <= assemblyRefCount; assemblyRefIndex++)
{
- R2RSection exceptionInfoSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_EXCEPTION_INFO];
- EHLookupTable = new EHLookupTable(Image, GetOffset(exceptionInfoSection.RelativeVirtualAddress), exceptionInfoSection.Size);
+ AssemblyReferenceHandle asmRefHandle = MetadataTokens.AssemblyReferenceHandle(assemblyRefIndex);
+ AssemblyReference asmRef = manifestReader.GetAssemblyReference(asmRefHandle);
+ string asmRefName = manifestReader.GetString(asmRef.Name);
+ ManifestReferenceAssemblies.Add(asmRefName);
}
+ }
+ }
- ImportSections = new List<R2RImportSection>();
- ImportCellNames = new Dictionary<int, string>();
- ParseImportSections();
-
- R2RMethods = new List<R2RMethod>();
- InstanceMethods = new List<InstanceMethod>();
+ if (R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_EXCEPTION_INFO))
+ {
+ R2RSection exceptionInfoSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_EXCEPTION_INFO];
+ EHLookupTable = new EHLookupTable(Image, GetOffset(exceptionInfoSection.RelativeVirtualAddress), exceptionInfoSection.Size);
+ }
- if (R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS))
- {
- int runtimeFunctionSize = CalculateRuntimeFunctionSize();
- R2RSection runtimeFunctionSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS];
+ ImportSections = new List<R2RImportSection>();
+ ImportCellNames = new Dictionary<int, string>();
+ ParseImportSections();
- uint nRuntimeFunctions = (uint)(runtimeFunctionSection.Size / runtimeFunctionSize);
- int runtimeFunctionOffset = GetOffset(runtimeFunctionSection.RelativeVirtualAddress);
- bool[] isEntryPoint = new bool[nRuntimeFunctions];
+ R2RMethods = new List<R2RMethod>();
+ InstanceMethods = new List<InstanceMethod>();
- // initialize R2RMethods
- ParseMethodDefEntrypoints(isEntryPoint);
- ParseInstanceMethodEntrypoints(isEntryPoint);
- ParseRuntimeFunctions(isEntryPoint, runtimeFunctionOffset, runtimeFunctionSize);
- }
+ if (R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS))
+ {
+ int runtimeFunctionSize = CalculateRuntimeFunctionSize();
+ R2RSection runtimeFunctionSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS];
- AvailableTypes = new List<string>();
- ParseAvailableTypes();
+ uint nRuntimeFunctions = (uint)(runtimeFunctionSection.Size / runtimeFunctionSize);
+ int runtimeFunctionOffset = GetOffset(runtimeFunctionSection.RelativeVirtualAddress);
+ bool[] isEntryPoint = new bool[nRuntimeFunctions];
- CompilerIdentifier = ParseCompilerIdentifier();
- }
+ // initialize R2RMethods
+ ParseMethodDefEntrypoints(isEntryPoint);
+ ParseInstanceMethodEntrypoints(isEntryPoint);
+ ParseRuntimeFunctions(isEntryPoint, runtimeFunctionOffset, runtimeFunctionSize);
}
+
+ AvailableTypes = new List<string>();
+ ParseAvailableTypes();
+
+ CompilerIdentifier = ParseCompilerIdentifier();
}
public bool InputArchitectureSupported()
@@ -470,7 +561,7 @@ namespace R2RDump
{
Console.WriteLine($"Warning: Could not parse GC Info for method: {method.SignatureString}");
}
-
+
}
}
else if (Machine == Machine.I386)
diff --git a/src/tools/r2rdump/R2RSection.cs b/src/tools/r2rdump/R2RSection.cs
index 0c715cc652..3ba922b609 100644
--- a/src/tools/r2rdump/R2RSection.cs
+++ b/src/tools/r2rdump/R2RSection.cs
@@ -27,7 +27,8 @@ namespace R2RDump
READYTORUN_SECTION_AVAILABLE_TYPES = 108,
READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS = 109,
READYTORUN_SECTION_INLINING_INFO = 110,
- READYTORUN_SECTION_PROFILEDATA_INFO = 111
+ READYTORUN_SECTION_PROFILEDATA_INFO = 111,
+ READYTORUN_SECTION_MANIFEST_METADATA = 112, // Added in v2.3
}
/// <summary>
diff --git a/src/tools/r2rdump/R2RSignature.cs b/src/tools/r2rdump/R2RSignature.cs
index 9bf44122b0..a3169530a8 100644
--- a/src/tools/r2rdump/R2RSignature.cs
+++ b/src/tools/r2rdump/R2RSignature.cs
@@ -41,9 +41,9 @@ namespace R2RDump
return formatter.EmitHandleName(handle, namespaceQualified, owningTypeOverride);
}
- public static string FormatSignature(DumpOptions options, R2RReader r2rReader, int imageOffset)
+ public static string FormatSignature(DumpOptions options, EcmaMetadataReader ecmaReader, int imageOffset)
{
- SignatureDecoder decoder = new SignatureDecoder(options, r2rReader, imageOffset);
+ SignatureDecoder decoder = new SignatureDecoder(options, ecmaReader, imageOffset);
string result = decoder.ReadR2RSignature();
return result;
}
@@ -74,6 +74,9 @@ namespace R2RDump
case HandleKind.TypeDefinition:
return EmitTypeDefinitionName((TypeDefinitionHandle)handle, namespaceQualified);
+ case HandleKind.FieldDefinition:
+ return EmitFieldDefinitionName((FieldDefinitionHandle)handle, namespaceQualified, owningTypeOverride);
+
default:
throw new NotImplementedException();
}
@@ -279,6 +282,26 @@ namespace R2RDump
return typeSpec.DecodeSignature<string, DisassemblingGenericContext>(this, genericContext);
}
+ /// <summary>
+ /// Emit the textual representation of a FieldDef metadata record.
+ /// </summary>
+ /// <param name="fieldDefHandle">Field definition handle to format</param>
+ /// <param name="namespaceQualified">True = display namespace information for the owning type</param>
+ /// <param name="owningTypeOverride">Owning type override when non-null</param>
+ /// <returns>Textual representation of the field declaration</returns>
+ private string EmitFieldDefinitionName(FieldDefinitionHandle fieldDefHandle, bool namespaceQualified, string owningTypeOverride)
+ {
+ FieldDefinition fieldDef = _metadataReader.GetFieldDefinition(fieldDefHandle);
+ DisassemblingGenericContext genericContext = new DisassemblingGenericContext(Array.Empty<string>(), Array.Empty<string>());
+ StringBuilder output = new StringBuilder();
+ output.Append(fieldDef.DecodeSignature<string, DisassemblingGenericContext>(this, genericContext));
+ output.Append(' ');
+ output.Append(EmitHandleName(fieldDef.GetDeclaringType(), namespaceQualified, owningTypeOverride));
+ output.Append('.');
+ output.Append(_metadataReader.GetString(fieldDef.Name));
+ return output.ToString();
+ }
+
private string EmitString(StringHandle handle)
{
return _metadataReader.GetString(handle);
@@ -291,9 +314,9 @@ namespace R2RDump
public class SignatureDecoder
{
/// <summary>
- /// Metadata reader is used to access the embedded MSIL metadata blob in the R2R file.
+ /// ECMA reader is used to access the embedded MSIL metadata blob in the R2R file.
/// </summary>
- private readonly MetadataReader _metadataReader;
+ private readonly EcmaMetadataReader _ecmaReader;
/// <summary>
/// Dump options are used to specify details of signature formatting.
@@ -318,26 +341,27 @@ namespace R2RDump
/// <summary>
/// Construct the signature decoder by storing the image byte array and offset within the array.
/// </summary>
- /// <param name="reader">R2RReader object representing the R2R PE file</param>
- /// <param name="offset">Signature offset within the array</param>
- /// <param name="options">Formatting options</param>
- public SignatureDecoder(DumpOptions options, R2RReader reader, int offset)
+ /// <param name="options">Dump options and paths</param>
+ /// <param name="ecmaReader">EcmaMetadataReader object representing the PE file containing the ECMA metadata</param>
+ /// <param name="offset">Signature offset within the PE file byte array</param>
+ public SignatureDecoder(DumpOptions options, EcmaMetadataReader ecmaReader, int offset)
{
- _metadataReader = reader.MetadataReader;
+ _ecmaReader = ecmaReader;
_options = options;
- _image = reader.Image;
+ _image = ecmaReader.Image;
_offset = offset;
}
/// <summary>
/// Construct the signature decoder by storing the image byte array and offset within the array.
/// </summary>
- /// <param name="metadataReader">Metadata reader for the R2R image</param>
+ /// <param name="options">Dump options and paths</param>
+ /// <param name="ecmaReader">Metadata reader for the R2R image</param>
/// <param name="signature">Signature to parse</param>
- /// <param name="offset">Optional signature offset within the signature byte array, 0 by default</param>
- public SignatureDecoder(DumpOptions options, MetadataReader metadataReader, byte[] signature, int offset = 0)
+ /// <param name="offset">Signature offset within the signature byte array</param>
+ public SignatureDecoder(DumpOptions options, EcmaMetadataReader ecmaReader, byte[] signature, int offset)
{
- _metadataReader = metadataReader;
+ _ecmaReader = ecmaReader;
_options = options;
_image = signature;
_offset = offset;
@@ -465,21 +489,34 @@ namespace R2RDump
/// <summary>
/// Parse the signature into a given output string builder.
/// </summary>
- /// <param name="builder"></param>
+ /// <param name="builder">Output signature builder</param>
private void ParseSignature(StringBuilder builder)
{
uint fixupType = ReadByte();
bool moduleOverride = (fixupType & (byte)CORCOMPILE_FIXUP_BLOB_KIND.ENCODE_MODULE_OVERRIDE) != 0;
+ SignatureDecoder moduleDecoder = this;
+
// Check first byte for a module override being encoded
if (moduleOverride)
{
- builder.Append("ENCODE_MODULE_OVERRIDE @ ");
fixupType &= ~(uint)CORCOMPILE_FIXUP_BLOB_KIND.ENCODE_MODULE_OVERRIDE;
- uint moduleIndex = ReadUInt();
- builder.Append(string.Format(" Index: {0:X2}", moduleIndex));
+ int moduleIndex = (int)ReadUInt();
+ EcmaMetadataReader refAsmEcmaReader = _ecmaReader.OpenReferenceAssembly(moduleIndex);
+ moduleDecoder = new SignatureDecoder(_options, refAsmEcmaReader, _image, _offset);
}
- switch ((ReadyToRunFixupKind)fixupType)
+ moduleDecoder.ParseSignature((ReadyToRunFixupKind)fixupType, builder);
+ _offset = moduleDecoder.Offset;
+ }
+
+ /// <summary>
+ /// Parse the signature with a given fixup type after module overrides have been resolved.
+ /// </summary>
+ /// <param name="fixupType">Fixup type to parse</param>
+ /// <param name="builder">Output signature builder</param>
+ private void ParseSignature(ReadyToRunFixupKind fixupType, StringBuilder builder)
+ {
+ switch (fixupType)
{
case ReadyToRunFixupKind.READYTORUN_FIXUP_ThisObjDictionaryLookup:
builder.Append("THISOBJ_DICTIONARY_LOOKUP @ ");
@@ -509,8 +546,8 @@ namespace R2RDump
break;
case ReadyToRunFixupKind.READYTORUN_FIXUP_FieldHandle:
- builder.Append("FIELD_HANDLE");
- // TODO
+ ParseField(builder);
+ builder.Append(" (FIELD_HANDLE)");
break;
@@ -520,10 +557,7 @@ namespace R2RDump
break;
case ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry_DefToken:
- if (!moduleOverride)
- {
- ParseMethodDefToken(builder, owningTypeOverride: null);
- }
+ ParseMethodDefToken(builder, owningTypeOverride: null);
builder.Append(" (METHOD_ENTRY");
builder.Append(_options.Naked ? ")" : "_DEF_TOKEN)");
break;
@@ -536,10 +570,7 @@ namespace R2RDump
case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry:
- if(!moduleOverride)
- {
- ParseMethod(builder);
- }
+ ParseMethod(builder);
builder.Append(" (VIRTUAL_ENTRY)");
break;
@@ -558,10 +589,7 @@ namespace R2RDump
case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry_Slot:
{
uint slot = ReadUInt();
- if (!moduleOverride)
- {
- ParseType(builder);
- }
+ ParseType(builder);
builder.Append($@" #{slot} (VIRTUAL_ENTRY_SLOT)");
}
@@ -893,7 +921,13 @@ namespace R2RDump
break;
case CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG:
- builder.Append("module_zapsig");
+ {
+ int moduleIndex = (int)ReadUInt();
+ EcmaMetadataReader refAsmReader = _ecmaReader.OpenReferenceAssembly(moduleIndex);
+ SignatureDecoder refAsmDecoder = new SignatureDecoder(_options, refAsmReader, _image, _offset);
+ refAsmDecoder.ParseType(builder);
+ _offset = refAsmDecoder.Offset;
+ }
break;
default:
@@ -919,7 +953,7 @@ namespace R2RDump
private void ParseTypeToken(StringBuilder builder)
{
uint token = ReadToken();
- builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)token)));
+ builder.Append(MetadataNameFormatter.FormatHandle(_ecmaReader.MetadataReader, MetadataTokens.Handle((int)token)));
}
/// <summary>
@@ -929,10 +963,20 @@ namespace R2RDump
private void ParseMethod(StringBuilder builder)
{
uint methodFlags = ReadUInt();
+
+ if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UnboxingStub) != 0)
+ {
+ builder.Append("[UNBOX] ");
+ }
+ if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0)
+ {
+ builder.Append("[INST] ");
+ }
+
string owningTypeOverride = null;
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0)
{
- SignatureDecoder owningTypeDecoder = new SignatureDecoder(_options, _metadataReader, _image, _offset);
+ SignatureDecoder owningTypeDecoder = new SignatureDecoder(_options, _ecmaReader, _image, _offset);
owningTypeOverride = owningTypeDecoder.ReadTypeSignature();
_offset = owningTypeDecoder._offset;
}
@@ -978,7 +1022,7 @@ namespace R2RDump
private void ParseMethodDefToken(StringBuilder builder, string owningTypeOverride)
{
uint methodDefToken = ReadUInt() | (uint)CorTokenType.mdtMethodDef;
- builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)methodDefToken), namespaceQualified: true, owningTypeOverride: owningTypeOverride));
+ builder.Append(MetadataNameFormatter.FormatHandle(_ecmaReader.MetadataReader, MetadataTokens.Handle((int)methodDefToken), namespaceQualified: true, owningTypeOverride: owningTypeOverride));
}
/// <summary>
@@ -989,7 +1033,7 @@ namespace R2RDump
private void ParseMethodRefToken(StringBuilder builder, string owningTypeOverride)
{
uint methodRefToken = ReadUInt() | (uint)CorTokenType.mdtMemberRef;
- builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)methodRefToken), namespaceQualified: false, owningTypeOverride: owningTypeOverride));
+ builder.Append(MetadataNameFormatter.FormatHandle(_ecmaReader.MetadataReader, MetadataTokens.Handle((int)methodRefToken), namespaceQualified: false, owningTypeOverride: owningTypeOverride));
}
/// <summary>
@@ -1015,7 +1059,7 @@ namespace R2RDump
{
fieldToken = ReadUInt() | (uint)CorTokenType.mdtFieldDef;
}
- builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)fieldToken), namespaceQualified: false, owningTypeOverride: owningTypeOverride));
+ builder.Append(MetadataNameFormatter.FormatHandle(_ecmaReader.MetadataReader, MetadataTokens.Handle((int)fieldToken), namespaceQualified: false, owningTypeOverride: owningTypeOverride));
}
/// <summary>
@@ -1405,7 +1449,7 @@ namespace R2RDump
{
uint rid = ReadUInt();
UserStringHandle stringHandle = MetadataTokens.UserStringHandle((int)rid);
- builder.Append(_metadataReader.GetUserString(stringHandle));
+ builder.Append(_ecmaReader.MetadataReader.GetUserString(stringHandle));
}
}
}
diff --git a/src/tools/r2rdump/TextDumper.cs b/src/tools/r2rdump/TextDumper.cs
index f31c903ec1..db4c5f3eeb 100644
--- a/src/tools/r2rdump/TextDumper.cs
+++ b/src/tools/r2rdump/TextDumper.cs
@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Xml;
@@ -356,6 +358,22 @@ namespace R2RDump
}
}
break;
+ case R2RSection.SectionType.READYTORUN_SECTION_MANIFEST_METADATA:
+ int assemblyRefCount = _r2r.MetadataReader.GetTableRowCount(TableIndex.AssemblyRef);
+ _writer.WriteLine($"MSIL AssemblyRef's ({assemblyRefCount} entries):");
+ for (int assemblyRefIndex = 1; assemblyRefIndex <= assemblyRefCount; assemblyRefIndex++)
+ {
+ AssemblyReference assemblyRef = _r2r.MetadataReader.GetAssemblyReference(MetadataTokens.AssemblyReferenceHandle(assemblyRefIndex));
+ string assemblyRefName = _r2r.MetadataReader.GetString(assemblyRef.Name);
+ _writer.WriteLine($"[ID 0x{assemblyRefIndex:X2}]: {assemblyRefName}");
+ }
+
+ _writer.WriteLine($"Manifest metadata AssemblyRef's ({_r2r.ManifestReferenceAssemblies.Count} entries):");
+ for (int manifestAsmIndex = 0; manifestAsmIndex < _r2r.ManifestReferenceAssemblies.Count; manifestAsmIndex++)
+ {
+ _writer.WriteLine($"[ID 0x{manifestAsmIndex + assemblyRefCount + 2:X2}]: {_r2r.ManifestReferenceAssemblies[manifestAsmIndex]}");
+ }
+ break;
}
}
diff --git a/src/tools/r2rdump/XmlDumper.cs b/src/tools/r2rdump/XmlDumper.cs
index 5f67e27fe2..25bbef5629 100644
--- a/src/tools/r2rdump/XmlDumper.cs
+++ b/src/tools/r2rdump/XmlDumper.cs
@@ -38,6 +38,10 @@ namespace R2RDump
_ignoredProperties.Add(typeof(RuntimeFunction), "UnwindRVA", attrs);
_ignoredProperties.Add(typeof(R2RSection), "RelativeVirtualAddress", attrs);
_ignoredProperties.Add(typeof(R2RSection), "Size", attrs);
+
+ XmlAttributes ignoreAlways = new XmlAttributes();
+ ignoreAlways.XmlIgnore = true;
+ _ignoredProperties.Add(typeof(R2RReader), "ImportCellNames", ignoreAlways);
}
internal override void Begin()