--- /dev/null
+// 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
+{
+ /// <summary>
+ /// Helper to read memory by 4-bit (half-byte) nibbles as is used for encoding
+ /// method fixups. More or less ported over from CoreCLR src\inc\nibblestream.h.
+ /// </summary>
+ class NibbleReader
+ {
+ /// <summary>
+ /// Special value in _nextNibble saying there's no next nibble and the next byte
+ /// must be read from the image.
+ /// </summary>
+ private const byte NoNextNibble = 0xFF;
+
+ /// <summary>
+ /// Byte array representing the PE file.
+ /// </summary>
+ private byte[] _image;
+
+ /// <summary>
+ /// Offset within the image.
+ /// </summary>
+ private int _offset;
+
+ /// <summary>
+ /// Value of the next nibble or 0xFF when there's no cached next nibble.
+ /// </summary>
+ private byte _nextNibble;
+
+ public NibbleReader(byte[] image, int offset)
+ {
+ _image = image;
+ _offset = offset;
+ _nextNibble = NoNextNibble;
+ }
+
+ public byte ReadNibble()
+ {
+ byte result;
+ if (_nextNibble != NoNextNibble)
+ {
+ result = _nextNibble;
+ _nextNibble = NoNextNibble;
+ }
+ else
+ {
+ _nextNibble = _image[_offset++];
+ result = (byte)(_nextNibble & 0x0F);
+ _nextNibble >>= 4;
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Read an unsigned int that was encoded via variable length nibble encoding
+ /// from CoreCLR NibbleWriter::WriteEncodedU32.
+ /// </summary>
+ public uint ReadUInt()
+ {
+ uint value = 0;
+
+ // The encoding is variably lengthed, with the high-bit of every nibble indicating whether
+ // there is another nibble in the value. Each nibble contributes 3 bits to the value.
+ uint nibble;
+ do
+ {
+ nibble = ReadNibble();
+ value = (value << 3) + (nibble & 0x7);
+ }
+ while ((nibble & 0x8) != 0);
+
+ return value;
+ }
+
+ /// <summary>
+ /// Read an encoded signed integer from the nibble reader. This uses the same unsigned
+ /// encoding, just left shifting the absolute value by one and filling in bit #0 with the sign bit.
+ /// </summary>
+ public int ReadInt()
+ {
+ uint unsignedValue = ReadUInt();
+ int signedValue = (int)(unsignedValue >> 1);
+ return ((unsignedValue & 1) != 0 ? -signedValue : signedValue);
+ }
+ }
+}
public struct ImportSectionEntry
{
+ public int StartOffset { get; set; }
public long Section { get; set; }
public uint SignatureRVA { get; set; }
- public uint Signature { get; set; }
- public ImportSectionEntry(long section, uint signatureRVA, uint signature)
+ public byte[] SignatureSample { get; set; }
+ public ImportSectionEntry(int startOffset, long section, uint signatureRVA, byte[] signatureSample)
{
+ StartOffset = startOffset;
Section = section;
SignatureRVA = signatureRVA;
- Signature = signature;
+ SignatureSample = signatureSample;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
- sb.AppendLine($"\tSection: 0x{Section:X8} ({Section})");
- sb.AppendLine($"\tSignatureRVA: 0x{SignatureRVA:X8} ({SignatureRVA})");
- sb.AppendLine($"\tSection: 0x{Signature:X8} ({Signature})");
+ sb.Append($@"+{StartOffset:X4} Section: 0x{Section:X8} SignatureRVA: 0x{SignatureRVA:X8} ");
+ foreach (byte b in SignatureSample)
+ {
+ sb.AppendFormat("{0:X2} ", b);
+ }
+ sb.Append("...");
return sb.ToString();
}
}
[XmlIgnore]
public GcInfo GcInfo { get; set; }
+ public FixupCell[] Fixups { get; set; }
+
/// <summary>
/// Maps all the generic parameters to the type in the instance
/// </summary>
/// <summary>
/// Extracts the method signature from the metadata by rid
/// </summary>
- public R2RMethod(MetadataReader mdReader, uint rid, int entryPointId, GenericElementTypes[] instanceArgs, uint[] tok)
+ public R2RMethod(MetadataReader mdReader, uint rid, int entryPointId, GenericElementTypes[] instanceArgs, uint[] tok, FixupCell[] fixups)
{
Token = _mdtMethodDef | rid;
Rid = rid;
argCount = signatureReader.ReadCompressedInteger();
}
+ Fixups = fixups;
+
DisassemblingTypeProvider provider = new DisassemblingTypeProvider();
if (IsGeneric && instanceArgs != null && tok != null)
{
InitGenericInstances(genericParams, instanceArgs, tok);
}
-
+
DisassemblingGenericContext genericContext = new DisassemblingGenericContext(new string[0], _genericParamInstanceMap.Values.ToArray());
Signature = _methodDef.DecodeSignature(provider, genericContext);
sb.AppendLine($"Rid: {Rid}");
sb.AppendLine($"EntryPointRuntimeFunctionId: {EntryPointRuntimeFunctionId}");
sb.AppendLine($"Number of RuntimeFunctions: {RuntimeFunctions.Count}");
+ if (Fixups != null)
+ {
+ sb.AppendLine($"Number of fixups: {Fixups.Count()}");
+ foreach (FixupCell cell in Fixups)
+ {
+ sb.AppendLine($" TableIndex {cell.TableIndex}, Offset {cell.CellOffset:X4}");
+ }
+ }
return sb.ToString();
}
E15 = 15,
}
+ /// <summary>
+ /// This structure represents a single precode fixup cell decoded from the
+ /// nibble-oriented per-method fixup blob. Each method entrypoint fixup
+ /// represents an array of cells that must be fixed up before the method
+ /// can start executing.
+ /// </summary>
+ public struct FixupCell
+ {
+ /// <summary>
+ /// Zero-based index of the import table within the import tables section.
+ /// </summary>
+ public uint TableIndex;
+
+ /// <summary>
+ /// Zero-based offset of the entry in the import table; it must be a multiple
+ /// of the target architecture pointer size.
+ /// </summary>
+ public uint CellOffset;
+
+ public FixupCell(uint tableIndex, uint cellOffset)
+ {
+ TableIndex = tableIndex;
+ CellOffset = cellOffset;
+ }
+ }
+
public class R2RReader
{
private readonly PEReader _peReader;
int offset = 0;
if (methodEntryPoints.TryGetAt(Image, rid - 1, ref offset))
{
- R2RMethod method = new R2RMethod(_mdReader, rid, GetEntryPointIdFromOffset(offset), null, null);
+ int runtimeFunctionId;
+ FixupCell[] fixups;
+ GetEntryPointInfoFromOffset(offset, out runtimeFunctionId, out fixups);
+ R2RMethod method = new R2RMethod(_mdReader, rid, runtimeFunctionId, null, null, fixups);
if (method.EntryPointRuntimeFunctionId < 0 || method.EntryPointRuntimeFunctionId >= isEntryPoint.Length)
{
}
}
- int id = GetEntryPointIdFromOffset((int)curParser.Offset);
- R2RMethod method = new R2RMethod(_mdReader, rid, id, args, tokens);
+ int runtimeFunctionId;
+ FixupCell[] fixups;
+ GetEntryPointInfoFromOffset((int)curParser.Offset, out runtimeFunctionId, out fixups);
+ R2RMethod method = new R2RMethod(_mdReader, rid, runtimeFunctionId, args, tokens, fixups);
if (method.EntryPointRuntimeFunctionId >= 0 && method.EntryPointRuntimeFunctionId < isEntryPoint.Length)
{
isEntryPoint[method.EntryPointRuntimeFunctionId] = true;
{
int rva = NativeReader.ReadInt32(Image, ref offset);
int sectionOffset = GetOffset(rva);
+ int startOffset = sectionOffset;
int size = NativeReader.ReadInt32(Image, ref offset);
R2RImportSection.CorCompileImportFlags flags = (R2RImportSection.CorCompileImportFlags)NativeReader.ReadUInt16(Image, ref offset);
byte type = NativeReader.ReadByte(Image, ref offset);
uint sigRva = 0;
while (sigRva != firstSigRva)
{
+ int entryOffset = sectionOffset - startOffset;
sigRva = NativeReader.ReadUInt32(Image, ref signatureOffset);
long section = NativeReader.ReadInt64(Image, ref sectionOffset);
int sigOff = GetOffset((int)sigRva);
- uint signature = NativeReader.ReadUInt32(Image, ref sigOff);
- entries.Add(new R2RImportSection.ImportSectionEntry(section, sigRva, signature));
+ int sigSampleLength = Math.Min(8, Image.Length - sigOff);
+ byte[] signatureSample = new byte[sigSampleLength];
+ Array.Copy(Image, sigOff, signatureSample, 0, sigSampleLength);
+ entries.Add(new R2RImportSection.ImportSectionEntry(entryOffset, section, sigRva, signatureSample));
}
}
break;
case R2RImportSection.CorCompileImportFlags.CORCOMPILE_IMPORT_FLAGS_PCODE:
for (int i = 0; i < entryCount; i++)
{
+ int entryOffset = sectionOffset - startOffset;
long section = NativeReader.ReadInt64(Image, ref sectionOffset);
uint sigRva = NativeReader.ReadUInt32(Image, ref signatureOffset);
int sigOff = GetOffset((int)sigRva);
- uint signature = NativeReader.ReadUInt32(Image, ref sigOff);
- entries.Add(new R2RImportSection.ImportSectionEntry(section, sigRva, signature));
+ int sigSampleLength = Math.Min(8, Image.Length - sigOff);
+ byte[] signatureSample = new byte[sigSampleLength];
+ Array.Copy(Image, sigOff, signatureSample, 0, sigSampleLength);
+ entries.Add(new R2RImportSection.ImportSectionEntry(entryOffset, section, sigRva, signatureSample));
}
break;
}
/// <summary>
/// Reads the method entrypoint from the offset. Used for non-generic methods
/// </summary>
- private int GetEntryPointIdFromOffset(int offset)
+ private void GetEntryPointInfoFromOffset(int offset, out int runtimeFunctionIndex, out FixupCell[] fixupCells)
{
+ fixupCells = null;
+
// 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);
NativeReader.DecodeUnsigned(Image, (uint)offset, ref val);
offset -= (int)val;
}
- // TODO: Dump fixups
+
+ fixupCells = DecodeFixupCells(offset);
id >>= 2;
}
id >>= 1;
}
- return (int)id;
+ runtimeFunctionIndex = (int)id;
+ }
+
+ private FixupCell[] DecodeFixupCells(int offset)
+ {
+ List<FixupCell> cells = new List<FixupCell>();
+ NibbleReader reader = new NibbleReader(Image, offset);
+
+ // The following algorithm has been loosely ported from CoreCLR,
+ // src\vm\ceeload.inl, BOOL Module::FixupDelayListAux
+ uint curTableIndex = reader.ReadUInt();
+
+ while (true)
+ {
+ uint fixupIndex = reader.ReadUInt(); // Accumulate the real rva from the delta encoded rva
+
+ while (true)
+ {
+ cells.Add(new FixupCell(curTableIndex, fixupIndex));
+
+ uint delta = reader.ReadUInt();
+
+ // Delta of 0 means end of entries in this table
+ if (delta == 0)
+ break;
+
+ fixupIndex += delta;
+ }
+
+ uint tableIndex = reader.ReadUInt();
+
+ if (tableIndex == 0)
+ break;
+
+ curTableIndex = curTableIndex + tableIndex;
+
+ } // Done with all entries in this table
+
+ return cells.ToArray();
}
}
}