// 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.Reflection.PortableExecutable;
+using System.Text;
namespace R2RDump
{
- class GCInfo
+ class GcInfo
{
public enum GcInfoHeaderFlags
{
RT_Illegal = 0xFF
};
+ public struct InterruptibleRange
+ {
+ public uint StartOffset { get; }
+ public uint StopOffset { get; }
+ public InterruptibleRange(uint start, uint stop)
+ {
+ StartOffset = start;
+ StopOffset = stop;
+ }
+ }
+
private const int GCINFO_VERSION = 2;
private const int MIN_GCINFO_VERSION_WITH_RETURN_KIND = 2;
private const int MIN_GCINFO_VERSION_WITH_REV_PINVOKE_FRAME = 2;
public uint SizeOfStackOutgoingAndScratchArea { get; }
public uint NumSafePoints { get; }
public uint NumInterruptibleRanges { get; }
+ public IEnumerable<uint> SafePointOffsets { get; }
+ public IEnumerable<InterruptibleRange> InterruptibleRanges { get; }
+ public GcSlotTable SlotTable { get; }
- public GCInfo(byte[] image, int offset, Machine machine, ushort majorVersion)
+ public GcInfo(byte[] image, int offset, Machine machine, ushort majorVersion)
{
- int sizeOfReturnKindSlim = 2;
- int sizeOfReturnKindFat = 2;
- int codeLengthEncBase = 8;
- int normPrologSizeEncBase = 5;
- int securityObjectStackSlotEncBase = 6;
- int gsCookieStackSlotEncBase = 6;
- int pspSymStackSlotEncBase = 6;
- int genericsInstContextStackSlotEncBase = 6;
- int stackBaseRegisterEncBase = 3;
- int sizeOfEditAndContinuePreservedAreaEncBase = 4;
- int reversePinvokeFrameEncBase = 6;
- int sizeOfStackAreaEncBase = 3;
- int numSafePointsEncBase = 3;
- int numInterruptibleRangesEncBase = 1;
- switch (machine)
- {
- case Machine.Amd64:
- sizeOfReturnKindFat = 4;
- numSafePointsEncBase = 2;
- break;
- case Machine.Arm:
- codeLengthEncBase = 7;
- securityObjectStackSlotEncBase = 5;
- gsCookieStackSlotEncBase = 5;
- pspSymStackSlotEncBase = 5;
- genericsInstContextStackSlotEncBase = 5;
- stackBaseRegisterEncBase = 1;
- sizeOfEditAndContinuePreservedAreaEncBase = 3;
- reversePinvokeFrameEncBase = 5;
- numInterruptibleRangesEncBase = 2;
- break;
- case Machine.Arm64:
- sizeOfReturnKindFat = 4;
- stackBaseRegisterEncBase = 2;
- break;
- case Machine.I386:
- codeLengthEncBase = 6;
- normPrologSizeEncBase = 4;
- sizeOfEditAndContinuePreservedAreaEncBase = 3;
- sizeOfStackAreaEncBase = 6;
- numSafePointsEncBase = 4;
- break;
- }
+ GcInfoTypes gcInfoTypes = new GcInfoTypes(machine);
SecurityObjectStackSlot = -1;
GSCookieStackSlot = -1;
GcInfoHeaderFlags headerFlags;
Version = ReadyToRunVersionToGcInfoVersion(majorVersion);
int bitOffset = offset * 8;
+ int initOffset = bitOffset;
bool slimHeader = (NativeReader.ReadBits(image, 1, ref bitOffset) == 0);
if (slimHeader)
if (Version >= MIN_GCINFO_VERSION_WITH_RETURN_KIND) // IsReturnKindAvailable
{
- int returnKindBits = (slimHeader) ? sizeOfReturnKindSlim : sizeOfReturnKindFat;
+ int returnKindBits = (slimHeader) ? gcInfoTypes.SIZE_OF_RETURN_KIND_SLIM : gcInfoTypes.SIZE_OF_RETURN_KIND_FAT;
ReturnKind = (ReturnKinds)NativeReader.ReadBits(image, returnKindBits, ref bitOffset);
}
- CodeLength = DenormalizeCodeLength(machine, (int)NativeReader.DecodeVarLengthUnsigned(image, codeLengthEncBase, ref bitOffset));
+ CodeLength = DenormalizeCodeLength(machine, (int)NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.CODE_LENGTH_ENCBASE, ref bitOffset));
if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_GS_COOKIE) != 0)
{
// Decode prolog/epilog information
- uint normPrologSize = NativeReader.DecodeVarLengthUnsigned(image, normPrologSizeEncBase, ref bitOffset) + 1;
- uint normEpilogSize = NativeReader.DecodeVarLengthUnsigned(image, normPrologSizeEncBase, ref bitOffset);
+ uint normPrologSize = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NORM_PROLOG_SIZE_ENCBASE, ref bitOffset) + 1;
+ uint normEpilogSize = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NORM_PROLOG_SIZE_ENCBASE, ref bitOffset);
ValidRangeStart = normPrologSize;
ValidRangeEnd = (uint)CodeLength - normEpilogSize;
else if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_SECURITY_OBJECT) != 0 || (headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK) != GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_NONE)
{
// Decode prolog information
- ValidRangeStart = NativeReader.DecodeVarLengthUnsigned(image, normPrologSizeEncBase, ref bitOffset) + 1;
+ ValidRangeStart = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NORM_PROLOG_SIZE_ENCBASE, ref bitOffset) + 1;
// satisfy asserts that assume m_GSCookieValidRangeStart != 0 ==> m_GSCookieValidRangeStart < m_GSCookieValidRangeEnd
ValidRangeEnd = ValidRangeStart + 1;
}
// Decode the offset to the security object.
if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_SECURITY_OBJECT) != 0)
{
- SecurityObjectStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, securityObjectStackSlotEncBase, ref bitOffset));
+ SecurityObjectStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.SECURITY_OBJECT_STACK_SLOT_ENCBASE, ref bitOffset));
}
if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_GS_COOKIE) != 0)
{
- GSCookieStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, gsCookieStackSlotEncBase, ref bitOffset));
+ GSCookieStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.GS_COOKIE_STACK_SLOT_ENCBASE, ref bitOffset));
}
if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_PSP_SYM) != 0)
{
- PSPSymStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, pspSymStackSlotEncBase, ref bitOffset));
+ PSPSymStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.PSP_SYM_STACK_SLOT_ENCBASE, ref bitOffset));
}
if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK) != GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_NONE)
{
- GenericsInstContextStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, genericsInstContextStackSlotEncBase, ref bitOffset));
+ GenericsInstContextStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE, ref bitOffset));
}
if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_STACK_BASE_REGISTER) != 0)
}
else
{
- StackBaseRegister = DenormalizeStackBaseRegister(machine, NativeReader.DecodeVarLengthUnsigned(image, stackBaseRegisterEncBase, ref bitOffset));
+ StackBaseRegister = DenormalizeStackBaseRegister(machine, NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.STACK_BASE_REGISTER_ENCBASE, ref bitOffset));
}
}
if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS) != 0)
{
- SizeOfEditAndContinuePreservedArea = NativeReader.DecodeVarLengthUnsigned(image, sizeOfEditAndContinuePreservedAreaEncBase, ref bitOffset);
+ SizeOfEditAndContinuePreservedArea = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE, ref bitOffset);
}
if (hasReversePInvokeFrame)
{
- ReversePInvokeFrameStackSlot = NativeReader.DecodeVarLengthSigned(image, reversePinvokeFrameEncBase, ref bitOffset);
+ ReversePInvokeFrameStackSlot = NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.REVERSE_PINVOKE_FRAME_ENCBASE, ref bitOffset);
}
// FIXED_STACK_PARAMETER_SCRATCH_AREA
}
else
{
- SizeOfStackOutgoingAndScratchArea = DenormalizeSizeOfStackArea(machine, NativeReader.DecodeVarLengthUnsigned(image, sizeOfStackAreaEncBase, ref bitOffset));
+ SizeOfStackOutgoingAndScratchArea = DenormalizeSizeOfStackArea(machine, NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.SIZE_OF_STACK_AREA_ENCBASE, ref bitOffset));
}
- // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
- NumSafePoints = NativeReader.DecodeVarLengthUnsigned(image, numSafePointsEncBase, ref bitOffset);
+ // PARTIALLY_intERRUPTIBLE_GC_SUPPORTED
+ NumSafePoints = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NUM_SAFE_POINTS_ENCBASE, ref bitOffset);
if (slimHeader)
{
}
else
{
- NumInterruptibleRanges = NativeReader.DecodeVarLengthUnsigned(image, numInterruptibleRangesEncBase, ref bitOffset);
+ NumInterruptibleRanges = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NUM_INTERRUPTIBLE_RANGES_ENCBASE, ref bitOffset);
}
- // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
- /*if (NumSafePoints != 0)
- {
- SafePointIndex = FindSafePoint(image, machine, InstructionOffset, ref bitOffset);
- }
- else
- {
- SafePointIndex = 0;
- }
+ // PARTIALLY_intERRUPTIBLE_GC_SUPPORTED
+ SafePointOffsets = EnumerateSafePoints(image, ref bitOffset);
uint numBitsPerOffset = CeilOfLog2(CodeLength);
- bitOffset += (int)(NumSafePoints* numBitsPerOffset);
-
+ bitOffset += (int)(NumSafePoints * numBitsPerOffset);
+ InterruptibleRanges = EnumerateinterruptibleRanges(image, gcInfoTypes.INTERRUPTIBLE_RANGE_DELTA1_ENCBASE, gcInfoTypes.INTERRUPTIBLE_RANGE_DELTA2_ENCBASE, ref bitOffset);
+
+ SlotTable = new GcSlotTable(image, machine, gcInfoTypes, ref bitOffset);
+ }
- EnumerateInterruptibleRanges(&SetIsInterruptibleCB, this);*/
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ string tab = " ";
+
+ sb.AppendLine($"{tab}Version: {Version}");
+ sb.AppendLine($"{tab}CodeLength: {CodeLength}");
+ sb.AppendLine($"{tab}ReturnKind: {Enum.GetName(typeof(ReturnKinds), ReturnKind)}");
+ sb.AppendLine($"{tab}ValidRangeStart: {ValidRangeStart}");
+ sb.AppendLine($"{tab}ValidRangeEnd: {ValidRangeEnd}");
+ if (SecurityObjectStackSlot != -1)
+ sb.AppendLine($"{tab}SecurityObjectStackSlot: {SecurityObjectStackSlot}");
+ if (GSCookieStackSlot != -1)
+ sb.AppendLine($"{tab}GSCookieStackSlot: {GSCookieStackSlot}");
+ if (PSPSymStackSlot != -1)
+ sb.AppendLine($"{tab}PSPSymStackSlot: {PSPSymStackSlot}");
+ if (GenericsInstContextStackSlot != -1)
+ sb.AppendLine($"{tab}GenericsInstContextStackSlot: {GenericsInstContextStackSlot}");
+ if (StackBaseRegister != 0xffffffff)
+ sb.AppendLine($"{tab}StackBaseRegister: {StackBaseRegister}");
+ if (SizeOfEditAndContinuePreservedArea != 0xffffffff)
+ sb.AppendLine($"{tab}SizeOfEditAndContinuePreservedArea: {SizeOfEditAndContinuePreservedArea}");
+ if (ReversePInvokeFrameStackSlot != -1)
+ sb.AppendLine($"{tab}ReversePInvokeFrameStackSlot: {ReversePInvokeFrameStackSlot}");
+ sb.AppendLine($"{tab}SizeOfStackOutgoingAndScratchArea: {SizeOfStackOutgoingAndScratchArea}");
+ sb.AppendLine($"{tab}NumSafePoints: {NumSafePoints}");
+ sb.AppendLine($"{tab}NumInterruptibleRanges: {NumInterruptibleRanges}");
+ sb.AppendLine($"{tab}SafePointOffsets:");
+ foreach (uint offset in SafePointOffsets)
+ {
+ sb.AppendLine($"{tab}{tab}{offset}");
+ }
+ sb.AppendLine($"{tab}SafePointOffsets:");
+ foreach (InterruptibleRange range in InterruptibleRanges)
+ {
+ sb.AppendLine($"{tab}{tab}start:{range.StartOffset}, end:{range.StopOffset}");
+ }
+ sb.AppendLine($"{tab}SlotTable:");
+ sb.Append(SlotTable.ToString());
+ return sb.ToString();
}
private int DenormalizeCodeLength(Machine target, int x)
return result;
}
-
- private uint FindSafePoint(byte[] image, Machine target, uint breakOffset, ref int currentPos)
+ private IEnumerable<uint> EnumerateSafePoints(byte[] image, ref int bitOffset)
{
- if (NumSafePoints == 0)
- return 0;
-
- int savedPos = currentPos;
+ List<uint> safePoints = new List<uint>();
uint numBitsPerOffset = CeilOfLog2(CodeLength);
- uint result = NumSafePoints;
+ for (int i = 0; i < NumSafePoints; i++)
+ {
+ uint normOffset = (uint)NativeReader.ReadBits(image, (int)numBitsPerOffset, ref bitOffset);
+ safePoints.Add(normOffset);
+ }
+ return safePoints;
+ }
+
+ private IEnumerable<InterruptibleRange> EnumerateinterruptibleRanges(byte[] image, int interruptibleRangeDelta1EncBase, int interruptibleRangeDelta2EncBase, ref int bitOffset)
+ {
+ List<InterruptibleRange> ranges = new List<InterruptibleRange>();
+ uint lastinterruptibleRangeStopOffset = 0;
- // Safepoints are encoded with a -1 adjustment
- // but normalizing them masks off the low order bit
- // Thus only bother looking if the address is odd
- if ((target != Machine.Arm && target != Machine.Arm64) || (breakOffset & 1) != 0)
+ for (uint i = 0; i < NumInterruptibleRanges; i++)
{
- int low = 0;
- int high = (int)NumSafePoints;
+ uint normStartDelta = NativeReader.DecodeVarLengthUnsigned(image, interruptibleRangeDelta1EncBase, ref bitOffset);
+ uint normStopDelta = NativeReader.DecodeVarLengthUnsigned(image, interruptibleRangeDelta2EncBase, ref bitOffset) + 1;
- while (low < high)
- {
- int mid = (low + high) / 2;
- currentPos = (int)(savedPos + mid * numBitsPerOffset);
- uint normOffset = (uint)NativeReader.ReadBits(image, (int)numBitsPerOffset, ref currentPos);
- if (normOffset == breakOffset)
- {
- result = (uint)mid;
- break;
- }
-
- if (normOffset < breakOffset)
- low = mid + 1;
- else
- high = mid;
- }
- }
+ uint rangeStartOffset = lastinterruptibleRangeStopOffset + normStartDelta;
+ uint rangeStopOffset = rangeStartOffset + normStopDelta;
+ ranges.Add(new InterruptibleRange(rangeStartOffset, rangeStopOffset));
- currentPos = (int)(savedPos + NumSafePoints * numBitsPerOffset);
- return result;
+ lastinterruptibleRangeStopOffset = rangeStopOffset;
+ }
+ return ranges;
}
/// <summary>
--- /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.
+
+using System.Reflection.PortableExecutable;
+
+namespace R2RDump
+{
+ class GcInfoTypes
+ {
+ public int SIZE_OF_RETURN_KIND_SLIM { get; } = 2;
+ public int SIZE_OF_RETURN_KIND_FAT { get; } = 2;
+ public int CODE_LENGTH_ENCBASE { get; } = 8;
+ public int NORM_PROLOG_SIZE_ENCBASE { get; } = 5;
+ public int SECURITY_OBJECT_STACK_SLOT_ENCBASE { get; } = 6;
+ public int GS_COOKIE_STACK_SLOT_ENCBASE { get; } = 6;
+ public int PSP_SYM_STACK_SLOT_ENCBASE { get; } = 6;
+ public int GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE { get; } = 6;
+ public int STACK_BASE_REGISTER_ENCBASE { get; } = 3;
+ public int SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE { get; } = 4;
+ public int REVERSE_PINVOKE_FRAME_ENCBASE { get; } = 6;
+ public int SIZE_OF_STACK_AREA_ENCBASE { get; } = 3;
+ public int NUM_SAFE_POINTS_ENCBASE { get; } = 3;
+ public int NUM_INTERRUPTIBLE_RANGES_ENCBASE { get; } = 1;
+ public int INTERRUPTIBLE_RANGE_DELTA1_ENCBASE { get; } = 6;
+ public int INTERRUPTIBLE_RANGE_DELTA2_ENCBASE { get; } = 6;
+
+ public int MAX_PREDECODED_SLOTS { get; } = 64;
+ public int NUM_REGISTERS_ENCBASE { get; } = 2;
+ public int NUM_STACK_SLOTS_ENCBASE { get; } = 2;
+ public int NUM_UNTRACKED_SLOTS_ENCBASE { get; } = 1;
+ public int REGISTER_ENCBASE { get; } = 3;
+ public int REGISTER_DELTA_ENCBASE { get; } = 2;
+ public int STACK_SLOT_ENCBASE { get; } = 6;
+ public int STACK_SLOT_DELTA_ENCBASE { get; } = 4;
+
+ public GcInfoTypes(Machine machine)
+ {
+ switch (machine)
+ {
+ case Machine.Amd64:
+ SIZE_OF_RETURN_KIND_FAT = 4;
+ NUM_SAFE_POINTS_ENCBASE = 2;
+ break;
+ case Machine.Arm:
+ CODE_LENGTH_ENCBASE = 7;
+ SECURITY_OBJECT_STACK_SLOT_ENCBASE = 5;
+ GS_COOKIE_STACK_SLOT_ENCBASE = 5;
+ PSP_SYM_STACK_SLOT_ENCBASE = 5;
+ GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE = 5;
+ STACK_BASE_REGISTER_ENCBASE = 1;
+ SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE = 3;
+ REVERSE_PINVOKE_FRAME_ENCBASE = 5;
+ NUM_INTERRUPTIBLE_RANGES_ENCBASE = 2;
+ INTERRUPTIBLE_RANGE_DELTA1_ENCBASE = 4;
+ NUM_STACK_SLOTS_ENCBASE = 3;
+ NUM_UNTRACKED_SLOTS_ENCBASE = 3;
+ REGISTER_ENCBASE = 2;
+ REGISTER_DELTA_ENCBASE = 1;
+ break;
+ case Machine.Arm64:
+ SIZE_OF_RETURN_KIND_FAT = 4;
+ STACK_BASE_REGISTER_ENCBASE = 2;
+ NUM_REGISTERS_ENCBASE = 3;
+ break;
+ case Machine.I386:
+ CODE_LENGTH_ENCBASE = 6;
+ NORM_PROLOG_SIZE_ENCBASE = 4;
+ SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE = 3;
+ SIZE_OF_STACK_AREA_ENCBASE = 6;
+ NUM_SAFE_POINTS_ENCBASE = 4;
+ INTERRUPTIBLE_RANGE_DELTA1_ENCBASE = 5;
+ INTERRUPTIBLE_RANGE_DELTA2_ENCBASE = 5;
+ NUM_REGISTERS_ENCBASE = 3;
+ NUM_STACK_SLOTS_ENCBASE = 5;
+ NUM_UNTRACKED_SLOTS_ENCBASE = 5;
+ REGISTER_DELTA_ENCBASE = 3;
+ break;
+ }
+ }
+
+}
+}
--- /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.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection.PortableExecutable;
+using System.Text;
+
+namespace R2RDump
+{
+ class GcSlotTable
+ {
+ public struct GcSlot
+ {
+ public int RegisterNumber { get; }
+ public GcStackSlot StackSlot { get; }
+ public GcSlotFlags Flags { get; }
+
+ public GcSlot(int registerNumber, GcStackSlot stack, GcSlotFlags flags)
+ {
+ RegisterNumber = registerNumber;
+ StackSlot = stack;
+ Flags = flags;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ string tab = " ";
+
+ if (StackSlot != null)
+ {
+ sb.AppendLine($"{tab}{tab}{tab}Stack:");
+ sb.AppendLine(StackSlot.ToString());
+ }
+ else
+ {
+ sb.AppendLine($"{tab}{tab}{tab}RegisterNumber: {RegisterNumber}");
+ }
+ sb.AppendLine($"{tab}{tab}{tab}Flags: {Flags}");
+
+ return sb.ToString();
+ }
+ }
+
+ public enum GcSlotFlags
+ {
+ GC_SLOT_BASE = 0x0,
+ GC_SLOT_INTERIOR = 0x1,
+ GC_SLOT_PINNED = 0x2,
+ GC_SLOT_UNTRACKED = 0x4,
+
+ // For internal use by the encoder/decoder
+ GC_SLOT_IS_REGISTER = 0x8,
+ GC_SLOT_IS_DELETED = 0x10,
+ };
+
+ public enum GcStackSlotBase
+ {
+ GC_CALLER_SP_REL = 0x0,
+ GC_SP_REL = 0x1,
+ GC_FRAMEREG_REL = 0x2,
+
+ GC_SPBASE_FIRST = GC_CALLER_SP_REL,
+ GC_SPBASE_LAST = GC_FRAMEREG_REL,
+ };
+
+ public class GcStackSlot
+ {
+ public int SpOffset { get; }
+ public GcStackSlotBase Base { get; }
+ public GcStackSlot(int spOffset, GcStackSlotBase stackSlotBase)
+ {
+ SpOffset = spOffset;
+ Base = stackSlotBase;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ string tab = " ";
+
+ sb.AppendLine($"{tab}{tab}{tab}{tab}SpOffset: {SpOffset}");
+ sb.Append($"{tab}{tab}{tab}{tab}Base: {Enum.GetName(typeof(GcStackSlotBase), Base)}");
+
+ return sb.ToString();
+ }
+ };
+
+ public uint NumRegisters { get; }
+ public uint NumStackSlots { get; }
+ public uint NumUntracked { get; }
+ public uint NumSlots { get; }
+ public List<GcSlot> GcSlots { get; }
+
+ public GcSlotTable(byte[] image, Machine machine, GcInfoTypes gcInfoTypes, ref int bitOffset)
+ {
+ if (NativeReader.ReadBits(image, 1, ref bitOffset) != 0)
+ {
+ NumRegisters = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NUM_REGISTERS_ENCBASE, ref bitOffset);
+ }
+ if (NativeReader.ReadBits(image, 1, ref bitOffset) != 0)
+ {
+ NumStackSlots = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NUM_STACK_SLOTS_ENCBASE, ref bitOffset);
+ NumUntracked = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NUM_UNTRACKED_SLOTS_ENCBASE, ref bitOffset);
+ }
+ NumSlots = NumRegisters + NumStackSlots + NumUntracked;
+
+ GcSlots = new List<GcSlot>();
+ if (NumRegisters > 0)
+ {
+ DecodeRegisters(image, gcInfoTypes, ref bitOffset);
+ }
+ if ((NumStackSlots > 0) && (GcSlots.Count < gcInfoTypes.MAX_PREDECODED_SLOTS))
+ {
+ DecodeStackSlots(image, machine, gcInfoTypes, NumStackSlots, ref bitOffset);
+ }
+ if ((NumUntracked > 0) && (GcSlots.Count < gcInfoTypes.MAX_PREDECODED_SLOTS))
+ {
+ DecodeStackSlots(image, machine, gcInfoTypes, NumUntracked, ref bitOffset);
+ }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ string tab = " ";
+
+ sb.AppendLine($"{tab}{tab}NumSlots({NumSlots}) = NumRegisters({NumRegisters}) + NumStackSlots({NumStackSlots}) + NumUntracked({NumUntracked})");
+ sb.AppendLine($"{tab}{tab}GcSlots:");
+ sb.AppendLine($"{tab}{tab}{tab}-------------------------");
+ foreach (GcSlot slot in GcSlots)
+ {
+ sb.Append(slot.ToString());
+ sb.AppendLine($"{tab}{tab}{tab}-------------------------");
+ }
+
+ return sb.ToString();
+ }
+
+ private int DenormalizeStackSlot(Machine target, int x)
+ {
+ switch (target)
+ {
+ case Machine.Amd64:
+ return (x << 3);
+ case Machine.Arm:
+ return (x << 2);
+ case Machine.Arm64:
+ return (x << 3);
+ }
+ return x;
+ }
+
+ private void DecodeRegisters(byte[] image, GcInfoTypes gcInfoTypes, ref int bitOffset)
+ {
+ // We certainly predecode the first register
+ uint regNum = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.REGISTER_ENCBASE, ref bitOffset);
+ GcSlotFlags flags = (GcSlotFlags)NativeReader.ReadBits(image, 2, ref bitOffset);
+ GcSlots.Add(new GcSlot((int)regNum, null, flags));
+
+ for (int i = 1; i < NumRegisters && i < gcInfoTypes.MAX_PREDECODED_SLOTS; i++)
+ {
+ if ((uint)flags != 0)
+ {
+ regNum = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.REGISTER_ENCBASE, ref bitOffset);
+ flags = (GcSlotFlags)NativeReader.ReadBits(image, 2, ref bitOffset);
+ }
+ else
+ {
+ uint regDelta = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.REGISTER_DELTA_ENCBASE, ref bitOffset) + 1;
+ regNum += regDelta;
+ }
+ GcSlots.Add(new GcSlot((int)regNum, null, flags));
+ }
+ }
+
+ private void DecodeStackSlots(byte[] image, Machine machine, GcInfoTypes gcInfoTypes, uint nSlots, ref int bitOffset)
+ {
+ // We have stack slots left and more room to predecode
+ GcStackSlotBase spBase = (GcStackSlotBase)NativeReader.ReadBits(image, 2, ref bitOffset);
+ int normSpOffset = NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.STACK_SLOT_ENCBASE, ref bitOffset);
+ int spOffset = DenormalizeStackSlot(machine, normSpOffset);
+ GcSlotFlags flags = (GcSlotFlags)NativeReader.ReadBits(image, 2, ref bitOffset);
+ GcSlots.Add(new GcSlot(-1, new GcStackSlot(spOffset, spBase), flags));
+
+ for (int i = 1; i < nSlots && GcSlots.Count < gcInfoTypes.MAX_PREDECODED_SLOTS; i++)
+ {
+ spBase = (GcStackSlotBase)NativeReader.ReadBits(image, 2, ref bitOffset);
+ if ((uint)flags != 0)
+ {
+ normSpOffset = NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.STACK_SLOT_ENCBASE, ref bitOffset);
+ spOffset = DenormalizeStackSlot(machine, normSpOffset);
+ flags = (GcSlotFlags)NativeReader.ReadBits(image, 2, ref bitOffset);
+ }
+ else
+ {
+ int normSpOffsetDelta = NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.STACK_SLOT_DELTA_ENCBASE, ref bitOffset);
+ normSpOffset += normSpOffsetDelta;
+ spOffset = DenormalizeStackSlot(machine, normSpOffset);
+ }
+ GcSlots.Add(new GcSlot(-1, new GcStackSlot(spOffset, spBase), flags));
+ }
+ }
+ }
+}