From b9ef2dbf7425ba152bf13c809dd4554f30fb953f Mon Sep 17 00:00:00 2001 From: Amy Yu Date: Wed, 6 Jun 2018 10:38:57 -0700 Subject: [PATCH] Read slot table, dump GcInfo Commit migrated from https://github.com/dotnet/coreclr/commit/fe757e679cc46abe0f67853eb2c42dd0c78035a6 --- src/coreclr/src/tools/r2rdump/GCInfo.cs | 212 +++++++++++++------------- src/coreclr/src/tools/r2rdump/GCInfoTypes.cs | 83 ++++++++++ src/coreclr/src/tools/r2rdump/GCSlotTable.cs | 207 +++++++++++++++++++++++++ src/coreclr/src/tools/r2rdump/NativeReader.cs | 2 + src/coreclr/src/tools/r2rdump/R2RDump.cs | 21 ++- src/coreclr/src/tools/r2rdump/R2RMethod.cs | 6 +- src/coreclr/src/tools/r2rdump/R2RReader.cs | 2 +- 7 files changed, 417 insertions(+), 116 deletions(-) create mode 100644 src/coreclr/src/tools/r2rdump/GCInfoTypes.cs create mode 100644 src/coreclr/src/tools/r2rdump/GCSlotTable.cs diff --git a/src/coreclr/src/tools/r2rdump/GCInfo.cs b/src/coreclr/src/tools/r2rdump/GCInfo.cs index f6dcb3b..c225fe4 100644 --- a/src/coreclr/src/tools/r2rdump/GCInfo.cs +++ b/src/coreclr/src/tools/r2rdump/GCInfo.cs @@ -2,11 +2,14 @@ // 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 { @@ -46,6 +49,17 @@ namespace R2RDump 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; @@ -65,52 +79,13 @@ namespace R2RDump public uint SizeOfStackOutgoingAndScratchArea { get; } public uint NumSafePoints { get; } public uint NumInterruptibleRanges { get; } + public IEnumerable SafePointOffsets { get; } + public IEnumerable 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; @@ -124,6 +99,7 @@ namespace R2RDump GcInfoHeaderFlags headerFlags; Version = ReadyToRunVersionToGcInfoVersion(majorVersion); int bitOffset = offset * 8; + int initOffset = bitOffset; bool slimHeader = (NativeReader.ReadBits(image, 1, ref bitOffset) == 0); if (slimHeader) @@ -144,17 +120,17 @@ namespace R2RDump 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; @@ -162,7 +138,7 @@ namespace R2RDump 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; } @@ -170,22 +146,22 @@ namespace R2RDump // 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) @@ -196,18 +172,18 @@ namespace R2RDump } 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 @@ -217,11 +193,11 @@ namespace R2RDump } 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) { @@ -229,26 +205,61 @@ namespace R2RDump } 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) @@ -316,44 +327,35 @@ namespace R2RDump return result; } - - private uint FindSafePoint(byte[] image, Machine target, uint breakOffset, ref int currentPos) + private IEnumerable EnumerateSafePoints(byte[] image, ref int bitOffset) { - if (NumSafePoints == 0) - return 0; - - int savedPos = currentPos; + List safePoints = new List(); 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 EnumerateinterruptibleRanges(byte[] image, int interruptibleRangeDelta1EncBase, int interruptibleRangeDelta2EncBase, ref int bitOffset) + { + List ranges = new List(); + 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; } /// diff --git a/src/coreclr/src/tools/r2rdump/GCInfoTypes.cs b/src/coreclr/src/tools/r2rdump/GCInfoTypes.cs new file mode 100644 index 0000000..46354f8 --- /dev/null +++ b/src/coreclr/src/tools/r2rdump/GCInfoTypes.cs @@ -0,0 +1,83 @@ +// 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; + } + } + +} +} diff --git a/src/coreclr/src/tools/r2rdump/GCSlotTable.cs b/src/coreclr/src/tools/r2rdump/GCSlotTable.cs new file mode 100644 index 0000000..3229069 --- /dev/null +++ b/src/coreclr/src/tools/r2rdump/GCSlotTable.cs @@ -0,0 +1,207 @@ +// 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 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(); + 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)); + } + } + } +} diff --git a/src/coreclr/src/tools/r2rdump/NativeReader.cs b/src/coreclr/src/tools/r2rdump/NativeReader.cs index eb5c777..bcd3d48 100644 --- a/src/coreclr/src/tools/r2rdump/NativeReader.cs +++ b/src/coreclr/src/tools/r2rdump/NativeReader.cs @@ -117,6 +117,8 @@ namespace R2RDump val ^= extraBits; } } + int mask = (1 << numBits) - 1; + val &= mask; bitOffset += numBits; return val; } diff --git a/src/coreclr/src/tools/r2rdump/R2RDump.cs b/src/coreclr/src/tools/r2rdump/R2RDump.cs index b287fa0..06ae90f 100644 --- a/src/coreclr/src/tools/r2rdump/R2RDump.cs +++ b/src/coreclr/src/tools/r2rdump/R2RDump.cs @@ -11,20 +11,21 @@ namespace R2RDump { class R2RDump { - private bool _help = false; + private bool _help; private IReadOnlyList _inputFilenames = Array.Empty(); private string _outputFilename = null; - private bool _raw = false; - private bool _header = false; - private bool _disasm = false; + private bool _raw; + private bool _header; + private bool _disasm; private IReadOnlyList _queries = Array.Empty(); private IReadOnlyList _keywords = Array.Empty(); private IReadOnlyList _runtimeFunctions = Array.Empty(); private IReadOnlyList _sections = Array.Empty(); - private bool _diff = false; + private bool _diff; private long _disassembler; - private bool _types = false; - private bool _unwind = false; + private bool _types; + private bool _unwind; + private bool _gc; private TextWriter _writer; private R2RDump() @@ -51,6 +52,7 @@ namespace R2RDump syntax.DefineOptionList("s|section", ref _sections, "Get section by keyword"); syntax.DefineOption("types", ref _types, "Dump available types"); syntax.DefineOption("unwind", ref _unwind, "Dump unwindInfo"); + syntax.DefineOption("gc", ref _gc, "Dump gcInfo and slot table"); syntax.DefineOption("diff", ref _diff, "Compare two R2R images (not yet implemented)"); // not yet implemented }); @@ -174,6 +176,11 @@ namespace R2RDump _writer.WriteLine("UnwindInfo:"); _writer.Write(rtf.UnwindInfo); } + if (_gc) + { + _writer.WriteLine("GcInfo:"); + _writer.Write(rtf.GcInfo); + } _writer.WriteLine(); } diff --git a/src/coreclr/src/tools/r2rdump/R2RMethod.cs b/src/coreclr/src/tools/r2rdump/R2RMethod.cs index 45dfd9d..115e834 100644 --- a/src/coreclr/src/tools/r2rdump/R2RMethod.cs +++ b/src/coreclr/src/tools/r2rdump/R2RMethod.cs @@ -44,16 +44,16 @@ namespace R2RDump public R2RMethod Method { get; } public UnwindInfo UnwindInfo { get; } - public GCInfo GCInfo { get; } + public GcInfo GcInfo { get; } - public RuntimeFunction(int id, int startRva, int endRva, int unwindRva, R2RMethod method, UnwindInfo unwindInfo, GCInfo gcInfo) + public RuntimeFunction(int id, int startRva, int endRva, int unwindRva, R2RMethod method, UnwindInfo unwindInfo, GcInfo gcInfo) { Id = id; StartAddress = startRva; UnwindRVA = unwindRva; Method = method; UnwindInfo = unwindInfo; - GCInfo = gcInfo; + GcInfo = gcInfo; Size = gcInfo.CodeLength; } diff --git a/src/coreclr/src/tools/r2rdump/R2RReader.cs b/src/coreclr/src/tools/r2rdump/R2RReader.cs index 5d16911..f641c73 100644 --- a/src/coreclr/src/tools/r2rdump/R2RReader.cs +++ b/src/coreclr/src/tools/r2rdump/R2RReader.cs @@ -219,7 +219,7 @@ namespace R2RDump int unwindOffset = GetOffset(unwindRva); UnwindInfo unwindInfo = new UnwindInfo(Image, unwindOffset); - GCInfo gcInfo = new GCInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion); + GcInfo gcInfo = new GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion); method.RuntimeFunctions.Add(new RuntimeFunction(runtimeFunctionId, startRva, endRva, unwindRva, method, unwindInfo, gcInfo)); runtimeFunctionId++; -- 2.7.4