From b5140fe582e674a1934ccad13b735837f4b35626 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 6 May 2025 16:25:13 -0400 Subject: [PATCH 01/56] Implement partial ExecutionManager GetFuncletStartAddress --- .../Extensions/IExecutionManagerExtensions.cs | 12 ++++++++ .../Contracts/IExecutionManager.cs | 3 +- .../ExecutionManager/ExecutionManagerCore.cs | 29 +++++++++++++++++-- .../ExecutionManager/ExecutionManager_1.cs | 3 +- .../ExecutionManager/ExecutionManager_2.cs | 3 +- .../Contracts/StackWalk/Context/Unwinder.cs | 2 +- 6 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/IExecutionManagerExtensions.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/IExecutionManagerExtensions.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/IExecutionManagerExtensions.cs new file mode 100644 index 00000000000000..303ff64eb444b1 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/IExecutionManagerExtensions.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; + +public static class IExecutionManagerExtensions +{ + public static bool IsFunclet(this IExecutionManager eman, CodeBlockHandle codeBlockHandle) + { + return eman.GetStartAddress(codeBlockHandle) != eman.GetFuncletStartAddress(codeBlockHandle); + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs index a977c79cab0c3a..7e8fdee4237c57 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs @@ -18,7 +18,8 @@ public interface IExecutionManager : IContract CodeBlockHandle? GetCodeBlockHandle(TargetCodePointer ip) => throw new NotImplementedException(); TargetPointer GetMethodDesc(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); TargetCodePointer GetStartAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); - TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle, TargetCodePointer ip) => throw new NotImplementedException(); + TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); + TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs index d8e1e501641711..477e630c928dfd 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs @@ -180,18 +180,41 @@ TargetCodePointer IExecutionManager.GetStartAddress(CodeBlockHandle codeInfoHand return info.StartAddress; } - TargetPointer IExecutionManager.GetUnwindInfo(CodeBlockHandle codeInfoHandle, TargetCodePointer ip) + TargetCodePointer IExecutionManager.GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) { if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); - RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, ip); + RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, codeInfoHandle.Address.Value); + if (range.Data == null) + throw new InvalidOperationException("Unable to get runtime function address"); + + JitManager jitManager = GetJitManager(range.Data); + TargetPointer runtimeFunctionPtr = jitManager.GetUnwindInfo(range, codeInfoHandle.Address.Value); + + if (runtimeFunctionPtr == TargetPointer.Null) + throw new InvalidOperationException("Unable to get runtime function address"); + + Data.RuntimeFunction runtimeFunction = _target.ProcessedData.GetOrAdd(runtimeFunctionPtr); + + // TODO(cdac): EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS, implement iterating over fragments until finding + // non-fragment RuntimeFunction + + return range.Data.RangeBegin + runtimeFunction.BeginAddress; + } + + TargetPointer IExecutionManager.GetUnwindInfo(CodeBlockHandle codeInfoHandle) + { + if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) + throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); + + RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, codeInfoHandle.Address.Value); if (range.Data == null) return TargetPointer.Null; JitManager jitManager = GetJitManager(range.Data); - return jitManager.GetUnwindInfo(range, ip); + return jitManager.GetUnwindInfo(range, codeInfoHandle.Address.Value); } TargetPointer IExecutionManager.GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs index 670b75dbe27b13..41f01dfdfac3de 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs @@ -17,6 +17,7 @@ internal ExecutionManager_1(Target target, Data.RangeSectionMap topRangeSectionM public CodeBlockHandle? GetCodeBlockHandle(TargetCodePointer ip) => _executionManagerCore.GetCodeBlockHandle(ip); public TargetPointer GetMethodDesc(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetMethodDesc(codeInfoHandle); public TargetCodePointer GetStartAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetStartAddress(codeInfoHandle); - public TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle, TargetCodePointer ip) => _executionManagerCore.GetUnwindInfo(codeInfoHandle, ip); + public TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetFuncletStartAddress(codeInfoHandle); + public TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfo(codeInfoHandle); public TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfoBaseAddress(codeInfoHandle); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs index 7378685ce67396..c9f5b23dacf376 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs @@ -17,6 +17,7 @@ internal ExecutionManager_2(Target target, Data.RangeSectionMap topRangeSectionM public CodeBlockHandle? GetCodeBlockHandle(TargetCodePointer ip) => _executionManagerCore.GetCodeBlockHandle(ip); public TargetPointer GetMethodDesc(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetMethodDesc(codeInfoHandle); public TargetCodePointer GetStartAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetStartAddress(codeInfoHandle); - public TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle, TargetCodePointer ip) => _executionManagerCore.GetUnwindInfo(codeInfoHandle, ip); + public TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetFuncletStartAddress(codeInfoHandle); + public TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfo(codeInfoHandle); public TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfoBaseAddress(codeInfoHandle); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/Unwinder.cs index 617c02af49b0e4..151614d1681182 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/Unwinder.cs @@ -153,7 +153,7 @@ private static unsafe void GetStackWalkInfo(ulong controlPC, void* pUnwindInfoBa } if ((nuint)pFuncEntry != 0) { - TargetPointer unwindInfo = eman.GetUnwindInfo(cbh, controlPC); + TargetPointer unwindInfo = eman.GetUnwindInfo(cbh); *(nuint*)pFuncEntry = (nuint)unwindInfo.Value; } } From 89e9505c35800b64f292b27c1929c8f6738d3eb2 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Fri, 16 May 2025 12:04:37 -0400 Subject: [PATCH 02/56] allow nativeAOT compilation on x86 --- src/native/managed/compile-native.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/managed/compile-native.proj b/src/native/managed/compile-native.proj index 6f0f88a25b449d..ccdcbd57f31a3d 100644 --- a/src/native/managed/compile-native.proj +++ b/src/native/managed/compile-native.proj @@ -23,7 +23,7 @@ false false - false + false true false From 02f14efe8577ec60e5de296778d19610fe034708 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 19 May 2025 14:16:52 -0400 Subject: [PATCH 03/56] implement GCInfo and RelativeAddress lookup --- .../debug/runtimeinfo/datadescriptor.h | 1 + .../Contracts/IExecutionManager.cs | 2 + .../TargetPointer.cs | 2 + .../ExecutionManagerCore.EEJitManager.cs | 26 +++++++++++++ ...ecutionManagerCore.ReadyToRunJitManager.cs | 38 +++++++++++++++++++ .../ExecutionManager/ExecutionManagerCore.cs | 26 +++++++++++++ .../ExecutionManager/ExecutionManager_1.cs | 2 + .../ExecutionManager/ExecutionManager_2.cs | 2 + .../Data/RealCodeHeader.cs | 2 + 9 files changed, 101 insertions(+) diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index aab39b5678ad99..ce914e14fa4ebf 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -635,6 +635,7 @@ CDAC_TYPE_END(RangeSection) CDAC_TYPE_BEGIN(RealCodeHeader) CDAC_TYPE_INDETERMINATE(RealCodeHeader) CDAC_TYPE_FIELD(RealCodeHeader, /*pointer*/, MethodDesc, offsetof(RealCodeHeader, phdrMDesc)) +CDAC_TYPE_FIELD(RealCodeHeader, /*pointer*/, GCInfo, offsetof(RealCodeHeader, phdrJitGCInfo)) #ifdef FEATURE_EH_FUNCLETS CDAC_TYPE_FIELD(RealCodeHeader, /*uint32*/, NumUnwindInfos, offsetof(RealCodeHeader, nUnwindInfos)) CDAC_TYPE_FIELD(RealCodeHeader, /* T_RUNTIME_FUNCTION */, UnwindInfos, offsetof(RealCodeHeader, unwindInfos)) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs index 7e8fdee4237c57..4a8faa684acfb6 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs @@ -21,6 +21,8 @@ public interface IExecutionManager : IContract TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); + void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) => throw new NotImplementedException(); + TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); } public readonly struct ExecutionManager : IExecutionManager diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetPointer.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetPointer.cs index d145347f3220a9..845e89b08bce06 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetPointer.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetPointer.cs @@ -16,6 +16,8 @@ namespace Microsoft.Diagnostics.DataContractReader; public static implicit operator ulong(TargetPointer p) => p.Value; public static implicit operator TargetPointer(ulong v) => new TargetPointer(v); + public static TargetPointer operator ++(TargetPointer p) => new TargetPointer(p.Value + 1); + public static TargetPointer operator --(TargetPointer p) => new TargetPointer(p.Value - 1); public static bool operator ==(TargetPointer left, TargetPointer right) => left.Value == right.Value; public static bool operator !=(TargetPointer left, TargetPointer right) => left.Value != right.Value; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs index 42982babbd0dc6..0a05adab6f8855 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs @@ -12,6 +12,8 @@ internal partial class ExecutionManagerCore : IExecutionManager { private sealed class EEJitManager : JitManager { + private const uint GCINFO_VERSION = 4; + private readonly INibbleMap _nibbleMap; private readonly RuntimeFunctionLookup _runtimeFunctions; public EEJitManager(Target target, INibbleMap nibbleMap) : base(target) @@ -20,6 +22,30 @@ public EEJitManager(Target target, INibbleMap nibbleMap) : base(target) _runtimeFunctions = RuntimeFunctionLookup.Create(target); } + public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion) + { + gcInfo = TargetPointer.Null; + gcVersion = 0; + + // EEJitManager::GetGCInfoToken + if (rangeSection.IsRangeList) + return; + + if (rangeSection.Data == null) + throw new ArgumentException(nameof(rangeSection)); + + TargetPointer codeStart = FindMethodCode(rangeSection, jittedCodeAddress); + if (codeStart == TargetPointer.Null) + return; + Debug.Assert(codeStart.Value <= jittedCodeAddress.Value); + + if (!GetRealCodeHeader(rangeSection, codeStart, out Data.RealCodeHeader? realCodeHeader)) + return; + + gcVersion = GCINFO_VERSION; + gcInfo = realCodeHeader.GCInfo; + } + public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) { info = null; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index 7b35e56f542d15..bbf51b9a3ba787 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -12,6 +12,8 @@ internal partial class ExecutionManagerCore : IExecutionManager { private sealed class ReadyToRunJitManager : JitManager { + private const uint GCINFO_VERSION = 4; + private readonly uint _runtimeFunctionSize; private readonly PtrHashMapLookup _hashMap; private readonly HotColdLookup _hotCold; @@ -25,6 +27,42 @@ public ReadyToRunJitManager(Target target) : base(target) _runtimeFunctions = RuntimeFunctionLookup.Create(target); } + public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion) + { + gcInfo = TargetPointer.Null; + gcVersion = 0; + + // ReadyToRunJitManager::GetGCInfoToken + if (rangeSection.Data == null) + throw new ArgumentException(nameof(rangeSection)); + + Debug.Assert(rangeSection.Data.R2RModule != TargetPointer.Null); + + Data.Module r2rModule = Target.ProcessedData.GetOrAdd(rangeSection.Data.R2RModule); + Debug.Assert(r2rModule.ReadyToRunInfo != TargetPointer.Null); + Data.ReadyToRunInfo r2rInfo = Target.ProcessedData.GetOrAdd(r2rModule.ReadyToRunInfo); + + // Check if address is in a thunk + if (IsStubCodeBlockThunk(rangeSection.Data, r2rInfo, jittedCodeAddress)) + return; + + // Find the relative address that we are looking for + TargetPointer addr = CodePointerUtils.AddressFromCodePointer(jittedCodeAddress, Target); + TargetPointer imageBase = rangeSection.Data.RangeBegin; + TargetPointer relativeAddr = addr - imageBase; + + uint index; + if (!_runtimeFunctions.TryGetRuntimeFunctionIndexForAddress(r2rInfo.RuntimeFunctions, r2rInfo.NumRuntimeFunctions, relativeAddr, out index)) + return; + + Data.RuntimeFunction runtimeFunction = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, index); + + TargetPointer unwindInfo = runtimeFunction.UnwindData + imageBase; + uint unwindDataSize = sizeof(uint); // TODO(cdac): This is platform specific and maybe needs its own contract. Current value is for x86 + gcInfo = unwindInfo + unwindDataSize; + gcVersion = GCINFO_VERSION; // TODO(cdac): This depends on the major version of the runtime. + } + public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) { // ReadyToRunJitManager::JitCodeToMethodInfo diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs index 477e630c928dfd..6d9b993dcfff6c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs @@ -66,6 +66,7 @@ protected JitManager(Target target) public abstract bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info); public abstract TargetPointer GetUnwindInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress); + public abstract void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion); } private sealed class RangeSection @@ -228,4 +229,29 @@ TargetPointer IExecutionManager.GetUnwindInfoBaseAddress(CodeBlockHandle codeInf return range.Data.RangeBegin; } + + void IExecutionManager.GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) + { + gcInfo = TargetPointer.Null; + gcVersion = 0; + + if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) + throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); + + RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, codeInfoHandle.Address.Value); + if (range.Data == null) + return; + + JitManager jitManager = GetJitManager(range.Data); + jitManager.GetGCInfo(range, codeInfoHandle.Address.Value, out gcInfo, out gcVersion); + } + + + TargetNUInt IExecutionManager.GetRelativeOffset(CodeBlockHandle codeInfoHandle) + { + if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) + throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); + + return info.RelativeOffset; + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs index 41f01dfdfac3de..2feda4180c2feb 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs @@ -20,4 +20,6 @@ internal ExecutionManager_1(Target target, Data.RangeSectionMap topRangeSectionM public TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetFuncletStartAddress(codeInfoHandle); public TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfo(codeInfoHandle); public TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfoBaseAddress(codeInfoHandle); + public void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) => _executionManagerCore.GetGCInfo(codeInfoHandle, out gcInfo, out gcVersion); + public TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetRelativeOffset(codeInfoHandle); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs index c9f5b23dacf376..356081e289b492 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs @@ -20,4 +20,6 @@ internal ExecutionManager_2(Target target, Data.RangeSectionMap topRangeSectionM public TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetFuncletStartAddress(codeInfoHandle); public TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfo(codeInfoHandle); public TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfoBaseAddress(codeInfoHandle); + public void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) => _executionManagerCore.GetGCInfo(codeInfoHandle, out gcInfo, out gcVersion); + public TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetRelativeOffset(codeInfoHandle); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RealCodeHeader.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RealCodeHeader.cs index 84ffab173b5f6c..35a90171ee6ba2 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RealCodeHeader.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RealCodeHeader.cs @@ -12,6 +12,7 @@ public RealCodeHeader(Target target, TargetPointer address) { Target.TypeInfo type = target.GetTypeInfo(DataType.RealCodeHeader); MethodDesc = target.ReadPointer(address + (ulong)type.Fields[nameof(MethodDesc)].Offset); + GCInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(GCInfo)].Offset); // Only available if FEATURE_EH_FUNCLETS is enabled. if (type.Fields.ContainsKey(nameof(NumUnwindInfos))) @@ -23,6 +24,7 @@ public RealCodeHeader(Target target, TargetPointer address) } public TargetPointer MethodDesc { get; init; } + public TargetPointer GCInfo { get; init; } public uint? NumUnwindInfos { get; init; } public TargetPointer? UnwindInfos { get; init; } } From 99a7f560ed645d63e9650af569f85ae6b306703d Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 21 May 2025 15:09:35 -0400 Subject: [PATCH 04/56] wip --- .../x86/InfoHdr.cs | 2 +- .../StackWalk/Context/X86/CallPattern.cs | 117 +++ .../StackWalk/Context/X86/GCInfoDecoder.cs | 713 ++++++++++++++++++ .../Context/X86/GCInfoTargetExtensions.cs | 64 ++ .../StackWalk/Context/X86/GCSlotTable.cs | 196 +++++ .../StackWalk/Context/X86/GCTransition.cs | 291 +++++++ .../StackWalk/Context/X86/InfoHdr.cs | 641 ++++++++++++++++ .../StackWalk/Context/X86/NoGcRegionTable.cs | 60 ++ .../StackWalk/Context/X86/X86Context.cs | 246 ++++++ .../StackWalk/Context/X86/X86Unwinder.cs | 509 +++++++++++++ .../Legacy/SOSDacImpl.cs | 11 + 11 files changed, 2849 insertions(+), 1 deletion(-) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/CallPattern.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoTargetExtensions.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCSlotTable.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCTransition.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/NoGcRegionTable.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Context.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs index 7852ebf962db60..d3d13a21a82ec8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs @@ -163,7 +163,7 @@ public class InfoHdrDecoder { private const uint HAS_UNTRACKED = 0xFFFFFFFF; private const uint HAS_GS_COOKIE_OFFSET = 0xFFFFFFFF; private const uint HAS_SYNC_OFFSET = 0xFFFFFFFF; - private const uint HAS_REV_PINVOKE_FRAME_OFFSET = 0xFFFFFFFF; + private const uint HAS_REV_PINVOKE_FRAME_OFFSET = 0xFFFFFFFF; // is this wrong?? private const uint HAS_NOGCREGIONS = 0xFFFFFFFF; private const uint YES = HAS_VARPTR; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/CallPattern.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/CallPattern.cs new file mode 100644 index 00000000000000..c9c39ecb3611ff --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/CallPattern.cs @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +internal static class CallPattern +{ + + /// + /// based on src\inc\gcdecoder.cpp decodeCallPattern + /// + public static void DecodeCallPattern(uint pattern, out uint argCnt, out uint regMask, out uint argMask, out uint codeDelta) + { + uint val = callPatternTable[pattern]; + byte[] fld = BitConverter.GetBytes(val); + argCnt = fld[0]; + regMask = fld[1]; // EBP,EBX,ESI,EDI + argMask = fld[2]; + codeDelta = fld[3]; + } + + /// + /// based on src\inc\gcdecoder.cpp callCommonDelta + /// + public static uint[] callCommonDelta = { 6, 8, 10, 12 }; + + /// + /// based on src\inc\gcdecoder.cpp callPatternTable + /// + private static uint[] callPatternTable = + { + 0x0a000200, // 30109 + 0x0c000200, // 22970 + 0x0c000201, // 19005 + 0x0a000300, // 12193 + 0x0c000300, // 10614 + 0x0e000200, // 10253 + 0x10000200, // 9746 + 0x0b000200, // 9698 + 0x0d000200, // 9625 + 0x08000200, // 8909 + 0x0c000301, // 8522 + 0x11000200, // 7382 + 0x0e000300, // 7357 + 0x12000200, // 7139 + 0x10000300, // 7062 + 0x11000300, // 6970 + 0x0a000201, // 6842 + 0x0a000100, // 6803 + 0x0f000200, // 6795 + 0x13000200, // 6559 + 0x08000300, // 6079 + 0x15000200, // 5874 + 0x0d000201, // 5492 + 0x0c000100, // 5193 + 0x0d000300, // 5165 + 0x23000200, // 5143 + 0x1b000200, // 5035 + 0x14000200, // 4872 + 0x0f000300, // 4850 + 0x0a000700, // 4781 + 0x09000200, // 4560 + 0x12000300, // 4496 + 0x16000200, // 4180 + 0x07000200, // 4021 + 0x09000300, // 4012 + 0x0c000700, // 3988 + 0x0c000600, // 3946 + 0x0e000100, // 3823 + 0x1a000200, // 3764 + 0x18000200, // 3744 + 0x17000200, // 3736 + 0x1f000200, // 3671 + 0x13000300, // 3559 + 0x0a000600, // 3214 + 0x0e000600, // 3109 + 0x08000201, // 2984 + 0x0b000300, // 2928 + 0x0a000301, // 2859 + 0x07000100, // 2826 + 0x13000100, // 2782 + 0x09000301, // 2644 + 0x19000200, // 2638 + 0x11000700, // 2618 + 0x21000200, // 2518 + 0x0d000202, // 2484 + 0x10000100, // 2480 + 0x0f000600, // 2413 + 0x14000300, // 2363 + 0x0c000500, // 2362 + 0x08000301, // 2285 + 0x20000200, // 2245 + 0x10000700, // 2240 + 0x0f000100, // 2236 + 0x1e000200, // 2214 + 0x0c000400, // 2193 + 0x16000300, // 2171 + 0x12000600, // 2132 + 0x22000200, // 2011 + 0x1d000200, // 2011 + 0x0c000f00, // 1996 + 0x0e000700, // 1971 + 0x0a000400, // 1970 + 0x09000201, // 1932 + 0x10000600, // 1903 + 0x15000300, // 1847 + 0x0a000101, // 1814 + 0x0a000b00, // 1771 + 0x0c000601, // 1737 + 0x09000700, // 1737 + 0x07000300, // 1684 + }; +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs new file mode 100644 index 00000000000000..5fd9d44d2e19ce --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs @@ -0,0 +1,713 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +[Flags] +public enum RegMask +{ + EAX = 0x1, + ECX = 0x2, + EDX = 0x4, + EBX = 0x8, + ESP = 0x10, + EBP = 0x20, + ESI = 0x40, + EDI = 0x80, + + NONE = 0x00, + RM_ALL = EAX | ECX | EDX | EBX | ESP | EBP | ESI | EDI, + RM_CALLEE_SAVED = EBP | EBX | ESI | EDI, + RM_CALLEE_TRASHED = RM_ALL & ~RM_CALLEE_SAVED, +} + +public record GCInfo +{ + private const uint byref_OFFSET_FLAG = 0x1; + + private readonly Target _target; + + public uint RelativeOffset { get; set; } + public uint MethodSize { get; set; } + public InfoHdr Header { get; set; } + + public bool IsInProlog => PrologOffset != unchecked((uint)-1); + public uint PrologOffset { get; set; } = unchecked((uint)-1); + + public bool IsInEpilog => EpilogOffset != unchecked((uint)-1); + public uint EpilogOffset { get; set; } = unchecked((uint)-1); + + public uint RawStackSize { get; set; } + + + /// + /// Count of the callee-saved registers, excluding the frame pointer. + /// This does not include EBP for EBP-frames and double-aligned-frames. + /// + public uint SavedRegsCountExclFP { get; set; } + public RegMask SavedRegsMask { get; set; } = RegMask.NONE; + + public NoGcRegionTable NoGCRegions { get; set; } = null!; + public GcSlotTable SlotTable { get; set; } = null!; + public Dictionary> Transitions { get; set; } = []; + + public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) + { + _target = target; + + TargetPointer offset = gcInfoAddress; + MethodSize = target.GCDecodeUnsigned(ref offset); + RelativeOffset = relativeOffset; + + Debug.Assert(relativeOffset >= 0); + Debug.Assert(relativeOffset <= MethodSize); + + Header = InfoHdr.DecodeHeader(target, ref offset, MethodSize); + uint infoHdrSize = (uint)(offset.Value - gcInfoAddress.Value); + + // Check if we are in the prolog + if (relativeOffset < Header.PrologSize) + { + PrologOffset = relativeOffset; + } + + // Check if we are in an epilog + foreach (uint epilogStart in Header.Epilogs) + { + if (relativeOffset > epilogStart && relativeOffset < epilogStart + Header.EpilogSize) + { + EpilogOffset = relativeOffset - epilogStart; + } + } + + // calculate raw stack size + uint frameDwordCount = Header.FrameSize; + RawStackSize = frameDwordCount * (uint)target.PointerSize; + + // calculate callee saved regs + uint savedRegsCount = 0; + RegMask savedRegs = RegMask.NONE; + + if (Header.EdiSaved) + { + savedRegsCount++; + savedRegs |= RegMask.EDI; + } + if (Header.EsiSaved) + { + savedRegsCount++; + savedRegs |= RegMask.ESI; + } + if (Header.EbxSaved) + { + savedRegsCount++; + savedRegs |= RegMask.EBX; + } + if (Header.EbpSaved) + { + savedRegsCount++; + savedRegs |= RegMask.EBP; + } + + SavedRegsCountExclFP = savedRegsCount; + SavedRegsMask = savedRegs; + if (Header.EbpFrame || Header.DoubleAlign) + { + Debug.Assert(Header.EbpSaved); + SavedRegsCountExclFP--; + } + + NoGCRegions = new NoGcRegionTable(target, Header, ref offset); + + SlotTable = new GcSlotTable(target, Header, ref offset); + + // Verify the argument table offset is consistent + Debug.Assert(offset.Value == gcInfoAddress.Value + infoHdrSize + Header.ArgTabOffset); + Transitions = new Dictionary>(); + if (Header.Interruptible) + { + GetTransitionsFullyInterruptible(ref offset); + } + else if (Header.EbpFrame) + { + GetTransitionsEbpFrame(ref offset); + } + else + { + GetTransitionsNoEbp(ref offset); + } + } + + private void AddNewTransition(BaseGcTransition transition) + { + if (!Transitions.TryGetValue(transition.CodeOffset, out List? value)) + { + value = []; + Transitions[transition.CodeOffset] = value; + } + + value.Add(transition); + } + + private void ArgEncoding(ref uint isPop, ref uint argOffs, ref uint argCnt, ref uint curOffs, ref bool isThis, ref bool iptr) + { + if (isPop != 0) + { + // A Pop of 0, means little-delta + + if (argOffs != 0) + { + AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt - argOffs, Action.POP, Header.EbpFrame)); + } + } + else + { + AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argOffs + 1, Action.PUSH, Header.EbpFrame, isThis, iptr)); + isThis = false; + iptr = false; + } + } + + private static RegMask ThreeBitEncodingToRegMask(byte val) => + (val & 0x7) switch + { + 0x0 => RegMask.EAX, + 0x1 => RegMask.ECX, + 0x2 => RegMask.EDX, + 0x3 => RegMask.EBX, + 0x4 => RegMask.ESP, + 0x5 => RegMask.EBP, + 0x6 => RegMask.ESI, + 0x7 => RegMask.EDI, + _ => throw new ArgumentOutOfRangeException(nameof(val), $"Not expected register value: {val}"), + }; + + private static RegMask TwoBitEncodingToRegMask(byte val) => + (val & 0x3) switch + { + 0x0 => RegMask.EDI, + 0x1 => RegMask.ESI, + 0x2 => RegMask.EBX, + 0x3 => RegMask.EBP, + _ => throw new ArgumentOutOfRangeException(nameof(val), $"Not expected register value: {val}"), + }; + + /// + /// based on GCDump::DumpGCTable + /// + private void GetTransitionsFullyInterruptible(ref TargetPointer offset) + { + uint argCnt = 0; + bool isThis = false; + bool iptr = false; + uint curOffs = 0; + + while (true) + { + uint isPop; + uint argOffs; + uint val = _target.Read(offset++); + + if ((val & 0x80) == 0) + { + /* A small 'regPtr' encoding */ + + curOffs += val & 0x7; + + Action isLive = Action.LIVE; + if ((val & 0x40) == 0) + isLive = Action.DEAD; + AddNewTransition(new GcTransitionRegister((int)curOffs, ThreeBitEncodingToRegMask((byte)((val >> 3) & 7)), isLive, isThis, iptr)); + + isThis = false; + iptr = false; + continue; + } + + /* This is probably an argument push/pop */ + + argOffs = (val & 0x38) >> 3; + + /* 6 [110] and 7 [111] are reserved for other encodings */ + + if (argOffs < 6) + { + /* A small argument encoding */ + + curOffs += val & 0x07; + isPop = val & 0x40; + + ArgEncoding(ref isPop, ref argOffs, ref argCnt, ref curOffs, ref isThis, ref iptr); + + continue; + } + else if (argOffs == 6) + { + if ((val & 0x40) != 0) + { + curOffs += (((val & 0x07) + 1) << 3); + } + else + { + // non-ptr arg push + + curOffs += (val & 0x07); + argCnt++; + AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt, Action.PUSH, Header.EbpFrame, false, false, false)); + } + + continue; + } + + // argOffs was 7 [111] which is reserved for the larger encodings + switch (val) + { + case 0xFF: + return; + case 0xBC: + isThis = true; + break; + case 0xBF: + iptr = true; + break; + case 0xB8: + val = _target.GCDecodeUnsigned(ref offset); + curOffs += val; + break; + case 0xF8: + case 0xFC: + isPop = val & 0x04; + argOffs = _target.GCDecodeUnsigned(ref offset); + ArgEncoding(ref isPop, ref argOffs, ref argCnt, ref curOffs, ref isThis, ref iptr); + break; + case 0xFD: + argOffs = _target.GCDecodeUnsigned(ref offset); + AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt, Action.KILL, Header.EbpFrame)); + break; + case 0xF9: + argOffs = _target.GCDecodeUnsigned(ref offset); + argCnt += argOffs; + break; + default: + throw new BadImageFormatException($"Unexpected special code {val}"); + } + } + } + + /// + /// based on GCDump::DumpGCTable + /// + private void GetTransitionsEbpFrame(ref TargetPointer offset) + { + while (true) + { + uint argMask = 0, byrefArgMask = 0; + uint regMask, byrefRegMask = 0; + + uint argCnt = 0; + TargetPointer argOffset = offset; + uint argTabSize; + + uint val, nxt; + uint curOffs = 0; + + // Get the next byte and check for a 'special' entry + uint encType = _target.Read(offset++); + GcTransitionCall? transition; + + switch (encType) + { + default: + // A tiny or small call entry + val = encType; + + if ((val & 0x80) == 0x00) + { + if ((val & 0x0F) != 0) + { + // A tiny call entry + + curOffs += (val & 0x0F); + regMask = (val & 0x70) >> 4; + argMask = 0; + } + else + { + RegMask reg; + if ((val & 0x10) != 0) + reg = RegMask.EDI; + else if ((val & 0x20) != 0) + reg = RegMask.ESI; + else if ((val & 0x40) != 0) + reg = RegMask.EBX; + else + throw new BadImageFormatException("Invalid register"); + transition = new GcTransitionCall((int)curOffs); + transition.CallRegisters.Add(new GcTransitionCall.CallRegister(reg, false)); + AddNewTransition(transition); + + continue; + } + } + else + { + // A small call entry + curOffs += (val & 0x7F); + val = _target.Read(offset++); + regMask = val >> 5; + argMask = val & 0x1F; + } + break; + + case 0xFD: // medium encoding + argMask = _target.Read(offset++); + val = _target.Read(offset++); + argMask |= (val & 0xF0) << 4; + nxt = _target.Read(offset++); + curOffs += (val & 0x0F) + ((nxt & 0x1F) << 4); + regMask = nxt >> 5; // EBX,ESI,EDI + break; + + case 0xF9: // medium encoding with byrefs + curOffs += _target.Read(offset++); + val = _target.Read(offset++); + argMask = val & 0x1F; + regMask = val >> 5; + val = _target.Read(offset++); + byrefArgMask = val & 0x1F; + byrefRegMask = val >> 5; + break; + case 0xFE: // large encoding + case 0xFA: // large encoding with byrefs + val = _target.Read(offset++); + regMask = val & 0x7; + byrefRegMask = val >> 4; + + curOffs += _target.Read(offset); + offset += 4; + argMask = _target.Read(offset); + offset += 4; + + if (encType == 0xFA) // read byrefArgMask + { + byrefArgMask = _target.Read(offset); + offset += 4; + } + break; + case 0xFB: // huge encoding + val = _target.Read(offset++); + regMask = val & 0x7; + byrefRegMask = val >> 4; + curOffs = _target.Read(offset); + offset += 4; + argCnt = _target.Read(offset); + offset += 4; + argTabSize = _target.Read(offset); + offset += 4; + argOffset = offset; + offset += argTabSize; + break; + case 0xFF: + return; + } + + /* + Here we have the following values: + + curOffs ... the code offset of the call + regMask ... mask of live pointer register variables + argMask ... bitmask of pushed pointer arguments + byrefRegMask ... byref qualifier for regMask + byrefArgMask ... byrer qualifier for argMask + */ + transition = new GcTransitionCall((int)curOffs, Header.EbpFrame, regMask, byrefRegMask); + AddNewTransition(transition); + + if (argCnt != 0) + { + do + { + val = _target.GCDecodeUnsigned(ref argOffset); + + uint stkOffs = val & ~byref_OFFSET_FLAG; + uint lowBit = val & byref_OFFSET_FLAG; + transition.PtrArgs.Add(new GcTransitionCall.PtrArg(stkOffs, lowBit)); + } + while (--argCnt > 0); + } + else + { + transition.ArgMask = argMask; + if (byrefArgMask != 0) + transition.IArgs = byrefArgMask; + } + } + } + + /// + /// based on GCDump::DumpGCTable + /// + private void SaveCallTransition(ref TargetPointer offset, uint val, uint curOffs, uint callRegMask, bool callPndTab, uint callPndTabCnt, uint callPndMask, uint lastSkip, ref uint imask) + { + uint iregMask, iargMask; + iregMask = imask & 0xF; + iargMask = imask >> 4; + + GcTransitionCall transition = new GcTransitionCall((int)curOffs, Header.EbpFrame, callRegMask, iregMask); + AddNewTransition(transition); + + if (callPndTab) + { + for (int i = 0; i < callPndTabCnt; i++) + { + uint pndOffs = _target.GCDecodeUnsigned(ref offset); + + uint stkOffs = val & ~byref_OFFSET_FLAG; + uint lowBit = val & byref_OFFSET_FLAG; + Console.WriteLine($"stkOffs: {stkOffs}, lowBit: {lowBit}"); + + transition.PtrArgs.Add(new GcTransitionCall.PtrArg(pndOffs, 0)); + } + } + else + { + if (callPndMask != 0) + transition.ArgMask = callPndMask; + if (iargMask != 0) + transition.IArgs = iargMask; + } + + Console.WriteLine($"lastSkip: {lastSkip}"); + imask /* = lastSkip */ = 0; + } + + private void GetTransitionsNoEbp(ref TargetPointer offset) + { + uint curOffs = 0; + uint lastSkip = 0; + uint imask = 0; + + for (; ; ) + { + uint val = _target.Read(offset++); + + if ((val & 0x80) == 0) + { + if ((val & 0x40) == 0) + { + if ((val & 0x20) == 0) + { + // push 000DDDDD push one item, 5-bit delta + curOffs += val & 0x1F; + AddNewTransition(new GcTransitionRegister((int)curOffs, RegMask.ESP, Action.PUSH)); + } + else + { + // push 00100000 [pushCount] ESP push multiple items + uint pushCount = _target.GCDecodeUnsigned(ref offset); + AddNewTransition(new GcTransitionRegister((int)curOffs, RegMask.ESP, Action.PUSH, false, false, (int)pushCount)); + } + } + else + { + uint popSize; + uint skip; + + if ((val & 0x3f) == 0) + { + // + // skip 01000000 [Delta] Skip arbitrary sized delta + // + skip = _target.GCDecodeUnsigned(ref offset); + curOffs += skip; + lastSkip = skip; + } + else + { + // pop 01CCDDDD pop CC items, 4-bit delta + popSize = (val & 0x30) >> 4; + skip = val & 0x0f; + curOffs += skip; + + if (popSize > 0) + { + AddNewTransition(new GcTransitionRegister((int)curOffs, RegMask.ESP, Action.POP, false, false, (int)popSize)); + } + else + lastSkip = skip; + } + } + } + else + { + uint callArgCnt = 0; + uint callRegMask; + bool callPndTab = false; + uint callPndMask = 0; + uint callPndTabCnt = 0, callPndTabSize = 0; + + switch ((val & 0x70) >> 4) + { + default: + // + // call 1PPPPPPP Call Pattern, P=[0..79] + // + CallPattern.DecodeCallPattern((val & 0x7f), out callArgCnt, out callRegMask, out callPndMask, out lastSkip); + curOffs += lastSkip; + SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + break; + + case 5: + // + // call 1101RRRR DDCCCMMM Call RegMask=RRRR,ArgCnt=CCC, + // ArgMask=MMM Delta=commonDelta[DD] + // + callRegMask = val & 0xf; // EBP,EBX,ESI,EDI + val = _target.Read(offset++); + callPndMask = val & 0x7; + callArgCnt = (val >> 3) & 0x7; + lastSkip = CallPattern.callCommonDelta[val >> 6]; + curOffs += lastSkip; + SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + break; + case 6: + // + // call 1110RRRR [ArgCnt] [ArgMask] + // Call ArgCnt,RegMask=RRR,ArgMask + // + callRegMask = val & 0xf; // EBP,EBX,ESI,EDI + callArgCnt = _target.GCDecodeUnsigned(ref offset); + callPndMask = _target.GCDecodeUnsigned(ref offset); + SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + break; + case 7: + switch (val & 0x0C) + { + case 0x00: + // iptr 11110000 [IPtrMask] Arbitrary Interior Pointer Mask + imask = _target.GCDecodeUnsigned(ref offset); + AddNewTransition(new IPtrMask((int)curOffs, imask)); + break; + + case 0x04: + AddNewTransition(new CalleeSavedRegister((int)curOffs, TwoBitEncodingToRegMask((byte)(val & 0x3)))); + break; + + case 0x08: + val = _target.Read(offset++); + callRegMask = val & 0xF; + imask = val >> 4; + lastSkip = _target.Read(offset); + offset += 4; + curOffs += lastSkip; + callArgCnt = _target.Read(offset); + offset += 4; + callPndTabCnt = _target.Read(offset); + offset += 4; + callPndTabSize = _target.Read(offset); + offset += 4; + callPndTab = true; + SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + break; + case 0x0C: + return; + default: + throw new BadImageFormatException("Invalid GC encoding"); + } + break; + } + Console.WriteLine($"CallArgCount: {callArgCnt}"); + Console.WriteLine($"CallPndTabCnt: {callPndTabSize}"); + } + } + } +} + +public class GCInfoDecoder(Target target) +{ + private readonly Target _target = target; + + // DecodeGCHDrInfo in src/coreclr/vm/gc_unwind_x86.inl + public TargetPointer DecodeGCHeaderInfo(TargetPointer gcInfoAddress, uint relativeOffset) + { + TargetPointer offset = gcInfoAddress; + uint methodSize = _target.GCDecodeUnsigned(ref offset); + + Debug.Assert(relativeOffset >= 0); + Debug.Assert(relativeOffset <= methodSize); + + return TargetPointer.Null; + + // InfoHdr infoHdr = InfoHdr.DecodeHeader(_target, ref offset, methodSize); + // uint infoHdrSize = (uint)(offset.Value - gcInfoAddress.Value); + + // gcInfo = new GCInfo + // { + // RelativeOffset = relativeOffset, + // MethodSize = methodSize, + // Header = infoHdr, + // }; + + // // Check if we are in the prolog + // if (relativeOffset < infoHdr.PrologSize) + // { + // gcInfo.PrologOffset = relativeOffset; + // } + + // // Check if we are in an epilog + // foreach (uint epilogStart in gcInfo.Header.Epilogs) + // { + // if (relativeOffset > epilogStart && relativeOffset < epilogStart + gcInfo.Header.EpilogSize) + // { + // gcInfo.EpilogOffset = relativeOffset - epilogStart; + // } + // } + + // // calculate raw stack size + // uint frameDwordCount = infoHdr.FrameSize; + // gcInfo.RawStackSize = frameDwordCount * (uint)_target.PointerSize; + + // // calculate callee saved regs + // uint savedRegsCount = 0; + // RegMask savedRegs = RegMask.NONE; + + // if (infoHdr.EdiSaved) + // { + // savedRegsCount++; + // savedRegs |= RegMask.EDI; + // } + // if (infoHdr.EsiSaved) + // { + // savedRegsCount++; + // savedRegs |= RegMask.ESI; + // } + // if (infoHdr.EbxSaved) + // { + // savedRegsCount++; + // savedRegs |= RegMask.EBX; + // } + // if (infoHdr.EbpSaved) + // { + // savedRegsCount++; + // savedRegs |= RegMask.EBP; + // } + + // gcInfo.SavedRegsCountExclFP = savedRegsCount; + // gcInfo.SavedRegsMask = savedRegs; + // if (infoHdr.EbpFrame || infoHdr.DoubleAlign) + // { + // Debug.Assert(infoHdr.EbpSaved); + // gcInfo.SavedRegsCountExclFP--; + // } + + // gcInfo.NoGCRegions = new NoGcRegionTable(_target, infoHdr, ref offset); + + // gcInfo.SlotTable = new GcSlotTable(_target, infoHdr, ref offset); + + // return TargetPointer.Null; + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoTargetExtensions.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoTargetExtensions.cs new file mode 100644 index 00000000000000..b7366f54242cc3 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoTargetExtensions.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +public static class GCInfoTargetExtensions +{ + public static uint GCDecodeUnsigned(this Target target, ref TargetPointer src) + { + TargetPointer begin = src; + byte b = target.Read(src++); + uint value = b & 0x7Fu; + while ((b & 0x80) != 0) + { + if ((src - begin) > 5) + { + throw new InvalidOperationException("Invalid variable-length integer encoding."); + } + + b = target.Read(src++); + value <<= 7; + value += b & 0x7Fu; + } + + return value; + } + + public static int GCDecodeSigned(this Target target, ref TargetPointer src) + { + TargetPointer begin = src; + byte b = target.Read(src++); + byte firstByte = b; + int value = b & 0x3F; + while ((b & 0x80) != 0) + { + if ((src - begin) > 5) + { + throw new InvalidOperationException("Invalid variable-length integer encoding."); + } + + b = target.Read(src++); + value <<= 7; + value += b & 0x7F; + } + + if ((firstByte & 0x40u) != 0) + { + value = -value; + } + + return value; + } + + /// + /// based on src\inc\gcdecoder.cpp decodeUDelta + /// + public static uint GCDecodeUDelta(this Target target, ref TargetPointer src, uint lastValue) + { + uint delta = target.GCDecodeUnsigned(ref src); + return lastValue + delta; + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCSlotTable.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCSlotTable.cs new file mode 100644 index 00000000000000..a70a94a83de60c --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCSlotTable.cs @@ -0,0 +1,196 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection.PortableExecutable; +using System.Text; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +public class GcSlotTable +{ + [Flags] + public enum GcSlotFlags + { + GC_SLOT_BASE = 0x0, + GC_SLOT_INTERIOR = 0x1, + GC_SLOT_PINNED = 0x2, + GC_SLOT_UNTRACKED = 0x4, + + GC_SLOT_INVALID = -1 + }; + + public class GcSlot + { + public int Index { get; set; } + public string Register { get; set; } + public int StackOffset { get; set; } + public int LowBits { get; set; } + public GcSlotFlags Flags { get; set; } + + public int BeginOffset { get; set; } + public int EndOffset { get; set; } + + public GcSlot(int index, string reg, int stkOffs, int lowBits, GcSlotFlags flags) + { + Index = index; + Register = reg; + StackOffset = stkOffs; + LowBits = lowBits; + Flags = flags; + + BeginOffset = -1; + EndOffset = -1; + } + + public GcSlot(int index, string reg, int beginOffs, int endOffs, int varOffs, int lowBits, GcSlotFlags flags) + { + Index = index; + Register = $"E{reg}P"; + StackOffset = varOffs; + LowBits = lowBits; + Flags = flags; + + BeginOffset = beginOffs; + EndOffset = endOffs; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + if ((Flags & GcSlotFlags.GC_SLOT_UNTRACKED) != 0) + { + if (StackOffset < 0) + { + sb.AppendLine($" [{Register}-{-StackOffset}]"); + } + else + { + sb.AppendLine($" [{Register}+{StackOffset}]"); + } + } + else + { + sb.AppendLine($" BeginOffset: {BeginOffset}"); + sb.AppendLine($" EndOffset: {EndOffset}"); + if (Register.Equals("BP")) + { + sb.AppendLine($" [{Register}-{-StackOffset}]"); + } + else + { + sb.AppendLine($" [{Register}+{-StackOffset}]"); + } + } + + sb.AppendLine($" Flags: {Flags}"); + + sb.Append($" LowBits: "); + if ((Flags & GcSlotFlags.GC_SLOT_UNTRACKED) != 0) + { + if ((LowBits & pinned_OFFSET_FLAG) != 0) sb.Append("pinned "); + if ((LowBits & byref_OFFSET_FLAG) != 0) sb.Append("byref "); + } + sb.AppendLine(); + + return sb.ToString(); + } + } + + private const uint OFFSET_MASK = 0x3; + private const uint byref_OFFSET_FLAG = 0x1; // the offset is an interior ptr + private const uint pinned_OFFSET_FLAG = 0x2; // the offset is a pinned ptr + + public List GcSlots { get; set; } + + + public GcSlotTable(Target target, InfoHdr header, ref TargetPointer offset) + { + GcSlots = new List(); + + DecodeUntracked(target, header, ref offset); + DecodeFrameVariableLifetimeTable(target, header, ref offset); + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + sb.AppendLine($" GcSlots:"); + sb.AppendLine($" -------------------------"); + foreach (GcSlot slot in GcSlots) + { + sb.Append(slot.ToString()); + sb.AppendLine($" -------------------------"); + } + + return sb.ToString(); + } + + /// + /// based on GCDump::DumpGCTable + /// + private void DecodeUntracked(Target target, InfoHdr header, ref TargetPointer offset) + { + uint calleeSavedRegs = 0; + if (header.DoubleAlign) + { + calleeSavedRegs = 0; + if (header.EdiSaved) calleeSavedRegs++; + if (header.EsiSaved) calleeSavedRegs++; + if (header.EbxSaved) calleeSavedRegs++; + } + + uint count = header.UntrackedCount; + int lastStkOffs = 0; + while (count-- > 0) + { + int stkOffsDelta; + int lowBits; + + char reg = header.EbpFrame ? 'B' : 'S'; + + stkOffsDelta = target.GCDecodeSigned(ref offset); + int stkOffs = lastStkOffs - stkOffsDelta; + lastStkOffs = stkOffs; + + lowBits = (int)OFFSET_MASK & stkOffs; + stkOffs = (int)((uint)stkOffs & ~OFFSET_MASK); + + if (header.DoubleAlign && + (uint)stkOffs >= sizeof(int) * (header.FrameSize + calleeSavedRegs)) + { + reg = 'B'; + stkOffs -= sizeof(int) * (int)(header.FrameSize + calleeSavedRegs); + } + + GcSlots.Add(new GcSlot(GcSlots.Count, $"E{reg}P", stkOffs, lowBits, GcSlotFlags.GC_SLOT_UNTRACKED)); + } + } + + /// + /// based on GCDump::DumpGCTable + /// + private void DecodeFrameVariableLifetimeTable(Target target, InfoHdr header, ref TargetPointer offset) + { + uint count = header.VarPtrTableSize; + uint curOffs = 0; + while (count-- > 0) + { + uint varOffs = target.GCDecodeUnsigned(ref offset); + uint begOffs = target.GCDecodeUDelta(ref offset, curOffs); + uint endOffs = target.GCDecodeUDelta(ref offset, begOffs); + + + uint lowBits = varOffs & 0x3; + varOffs &= ~OFFSET_MASK; + + curOffs = begOffs; + + string reg = header.EbpFrame ? "BP" : "SP"; + GcSlots.Add(new GcSlot(GcSlots.Count, reg, (int)begOffs, (int)endOffs, (int)varOffs, (int)lowBits, GcSlotFlags.GC_SLOT_BASE)); + } + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCTransition.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCTransition.cs new file mode 100644 index 00000000000000..a158924311f910 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCTransition.cs @@ -0,0 +1,291 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +public enum Action +{ + POP = 0x00, + PUSH = 0x01, + KILL = 0x02, + LIVE = 0x03, + DEAD = 0x04 +} + +public abstract class BaseGcTransition +{ + public int CodeOffset { get; set; } + + public BaseGcTransition() { } + + public BaseGcTransition(int codeOffset) + { + CodeOffset = codeOffset; + } +} + +public class CalleeSavedRegister : BaseGcTransition +{ + public RegMask Register { get; set; } + + public CalleeSavedRegister() { } + + public CalleeSavedRegister(int codeOffset, RegMask reg) + : base(codeOffset) + { + Register = reg; + } + + public override string ToString() + { + return $"thisptr in {Register}"; + } +} + +public class IPtrMask : BaseGcTransition +{ + public uint IMask { get; set; } + + public IPtrMask() { } + + public IPtrMask(int codeOffset, uint imask) + : base(codeOffset) + { + IMask = imask; + } + + public override string ToString() + { + return $"iptrMask: {IMask}"; + } +} + +public class GcTransitionRegister : BaseGcTransition +{ + public RegMask Register { get; set; } + public Action IsLive { get; set; } + public int PushCountOrPopSize { get; set; } + public bool IsThis { get; set; } + public bool Iptr { get; set; } + + public GcTransitionRegister() { } + + public GcTransitionRegister(int codeOffset, RegMask reg, Action isLive, bool isThis = false, bool iptr = false, int pushCountOrPopSize = -1) + : base(codeOffset) + { + Register = reg; + IsLive = isLive; + PushCountOrPopSize = pushCountOrPopSize; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + if (IsLive == Action.LIVE) + { + sb.Append($"reg {Register} becoming live"); + } + else if (IsLive == Action.DEAD) + { + sb.Append($"reg {Register} becoming dead"); + } + else + { + sb.Append((IsLive == Action.PUSH ? "push" : "pop") + $" {Register}"); + if (PushCountOrPopSize != -1) + sb.Append($" {PushCountOrPopSize}"); + } + + if (IsThis) + sb.Append(" 'this'"); + if (Iptr) + sb.Append(" (iptr)"); + + return sb.ToString(); + } +} + +public class GcTransitionPointer : BaseGcTransition +{ + private bool _isEbpFrame; + public uint ArgOffset { get; set; } + public uint ArgCount { get; set; } + public Action Act { get; set; } + public bool IsPtr { get; set; } + public bool IsThis { get; set; } + public bool Iptr { get; set; } + + public GcTransitionPointer() { } + + public GcTransitionPointer(int codeOffset, uint argOffs, uint argCnt, Action act, bool isEbpFrame, bool isThis = false, bool iptr = false, bool isPtr = true) + : base(codeOffset) + { + _isEbpFrame = isEbpFrame; + CodeOffset = codeOffset; + ArgOffset = argOffs; + ArgCount = argCnt; + Act = act; + IsPtr = isPtr; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + if (Act == Action.KILL) + { + sb.Append($"kill args {ArgOffset}"); + } + else + { + if (Act == Action.POP) + { + sb.Append($"pop "); + } + else + { + sb.Append($"push "); + } + if (IsPtr) + { + sb.Append($"{ArgOffset}"); + if (!_isEbpFrame) + { + sb.Append($" args ({ArgCount})"); + } + else if (Act == Action.POP) + { + sb.Append(" ptrs"); + } + + if (IsThis) + sb.Append(" 'this'"); + if (Iptr) + sb.Append(" (iptr)"); + } + else + { + sb.Append("non-pointer"); + sb.Append($" ({ArgCount})"); + } + } + + return sb.ToString(); + } +} + +public class GcTransitionCall : BaseGcTransition +{ + public struct CallRegister + { + public RegMask Register { get; set; } + public bool IsByRef { get; set; } + + public CallRegister(RegMask reg, bool isByRef) + { + Register = reg; + IsByRef = isByRef; + } + } + + public struct PtrArg + { + public uint StackOffset { get; set; } + public uint LowBit { get; set; } + + public PtrArg(uint stackOffset, uint lowBit) + { + StackOffset = stackOffset; + LowBit = lowBit; + } + } + + public List CallRegisters { get; set; } + public List PtrArgs { get; set; } + public uint ArgMask { get; set; } + public uint IArgs { get; set; } + + public GcTransitionCall(int codeOffset) + : base(codeOffset) + { + CallRegisters = new List(); + PtrArgs = new List(); + ArgMask = 0; + IArgs = 0; + } + + public GcTransitionCall(int codeOffset, bool isEbpFrame, uint regMask, uint byRefRegMask) + : base(codeOffset) + { + CallRegisters = new List(); + PtrArgs = new List(); + if ((regMask & 1) != 0) + { + RegMask reg = RegMask.EDI; + bool isByRef = (byRefRegMask & 1) != 0; + CallRegisters.Add(new CallRegister(reg, isByRef)); + } + if ((regMask & 2) != 0) + { + RegMask reg = RegMask.ESI; + bool isByRef = (byRefRegMask & 2) != 0; + CallRegisters.Add(new CallRegister(reg, isByRef)); + } + if ((regMask & 4) != 0) + { + RegMask reg = RegMask.EBX; + bool isByRef = (byRefRegMask & 4) != 0; + CallRegisters.Add(new CallRegister(reg, isByRef)); + } + if (!isEbpFrame) + { + if ((regMask & 8) != 0) + { + RegMask reg = RegMask.EBP; + CallRegisters.Add(new CallRegister(reg, false)); + } + } + ArgMask = 0; + IArgs = 0; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + sb.Append("call [ "); + foreach (CallRegister reg in CallRegisters) + { + sb.Append($"{reg.Register}"); + if (reg.IsByRef) + sb.Append("(byref)"); + sb.Append(' '); + } + + if (PtrArgs.Count > 0) + { + sb.Append(" ] ptrArgs=[ "); + foreach (PtrArg ptrArg in PtrArgs) + { + sb.Append($"{ptrArg.StackOffset}"); + if (ptrArg.LowBit != 0) + sb.Append('i'); + sb.Append(' '); + } + sb.Append(" ]"); + } + else + { + sb.Append($" ] argMask={ArgMask}"); + if (IArgs != 0) + sb.Append($" (iargs={IArgs})"); + } + + return sb.ToString(); + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs new file mode 100644 index 00000000000000..234d036cfd02b6 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs @@ -0,0 +1,641 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +/// +/// based on src\inc\gcinfotypes.h InfoHdrSmall +/// +public record struct InfoHdr +{ + public byte PrologSize { get; set; } + public byte EpilogSize { get; set; } + + public byte EpilogCount { get; set; } // 0 - 7 + public bool EpilogAtEnd { get; set; } + public bool EdiSaved { get; set; } + public bool EsiSaved { get; set; } + public bool EbxSaved { get; set; } + public bool EbpSaved { get; set; } + + public bool EbpFrame { get; set; } + public bool Interruptible { get; set; } + public bool DoubleAlign { get; set; } + public bool Security { get; set; } + public bool Handlers { get; set; } + public bool LocalAlloc { get; set; } + public bool EditAndContinue { get; set; } + public bool VarArgs { get; set; } + + public bool ProfCallbacks { get; set; } + public bool GenericsContext { get; set; } + public bool GenericsContextIsMethodDesc { get; set; } + public ReturnKinds ReturnKind { get; set; } + + public ushort ArgCount { get; set; } + public uint FrameSize { get; set; } + public uint UntrackedCount { get; set; } + public uint VarPtrTableSize { get; set; } + + // Fields not set by the initial table encoding + public uint GsCookieOffset { get; set; } = 0; + public uint SyncStartOffset { get; set; } = 0; + public uint SyndEndOffset { get; set; } = 0; + public uint RevPInvokeOffset { get; set; } = unchecked((uint)-1); + public uint NoGCRegionCount { get; set; } = 0; + + public bool HasArgTabOffset { get; set; } + public uint ArgTabOffset { get; set; } + public List Epilogs { get; set; } + + #region Adjustments + + private const byte SET_FRAMESIZE_MAX = 7; + private const byte SET_ARGCOUNT_MAX = 8; // Change to 6 + private const byte SET_PROLOGSIZE_MAX = 16; + private const byte SET_EPILOGSIZE_MAX = 10; // Change to 6 + private const byte SET_EPILOGCNT_MAX = 4; + private const byte SET_UNTRACKED_MAX = 3; + private const byte SET_RET_KIND_MAX = 3; // 2 bits for ReturnKind + private const byte SET_NOGCREGIONS_MAX = 4; + private const byte ADJ_ENCODING_MAX = 0x7f; // Maximum valid encoding in a byte + // Also used to mask off next bit from each encoding byte. + private const byte MORE_BYTES_TO_FOLLOW = 0x80; // If the High-bit of a header or adjustment byte + // is set, then there are more adjustments to follow. + + // + // Enum to define codes that are used to incrementally adjust the InfoHdr structure. + // First set of opcodes + private enum InfoHdrAdjust : byte + { + + SET_FRAMESIZE = 0, // 0x00 + SET_ARGCOUNT = SET_FRAMESIZE + SET_FRAMESIZE_MAX + 1, // 0x08 + SET_PROLOGSIZE = SET_ARGCOUNT + SET_ARGCOUNT_MAX + 1, // 0x11 + SET_EPILOGSIZE = SET_PROLOGSIZE + SET_PROLOGSIZE_MAX + 1, // 0x22 + SET_EPILOGCNT = SET_EPILOGSIZE + SET_EPILOGSIZE_MAX + 1, // 0x2d + SET_UNTRACKED = SET_EPILOGCNT + (SET_EPILOGCNT_MAX + 1) * 2, // 0x37 + + FIRST_FLIP = SET_UNTRACKED + SET_UNTRACKED_MAX + 1, + + FLIP_EDI_SAVED = FIRST_FLIP, // 0x3b + FLIP_ESI_SAVED, // 0x3c + FLIP_EBX_SAVED, // 0x3d + FLIP_EBP_SAVED, // 0x3e + FLIP_EBP_FRAME, // 0x3f + FLIP_INTERRUPTIBLE, // 0x40 + FLIP_DOUBLE_ALIGN, // 0x41 + FLIP_SECURITY, // 0x42 + FLIP_HANDLERS, // 0x43 + FLIP_LOCALLOC, // 0x44 + FLIP_EDITnCONTINUE, // 0x45 + FLIP_VAR_PTR_TABLE_SZ, // 0x46 Flip whether a table-size exits after the header encoding + FFFF_UNTRACKED_CNT, // 0x47 There is a count (>SET_UNTRACKED_MAX) after the header encoding + FLIP_VARARGS, // 0x48 + FLIP_PROF_CALLBACKS, // 0x49 + FLIP_HAS_GS_COOKIE, // 0x4A - The offset of the GuardStack cookie follows after the header encoding + FLIP_SYNC, // 0x4B + FLIP_HAS_GENERICS_CONTEXT, // 0x4C + FLIP_GENERICS_CONTEXT_IS_METHODDESC, // 0x4D + FLIP_REV_PINVOKE_FRAME, // 0x4E + NEXT_OPCODE, // 0x4F -- see next Adjustment enumeration + NEXT_FOUR_START = 0x50, + NEXT_FOUR_FRAMESIZE = 0x50, + NEXT_FOUR_ARGCOUNT = 0x60, + NEXT_THREE_PROLOGSIZE = 0x70, + NEXT_THREE_EPILOGSIZE = 0x78 + }; + + // Second set of opcodes, when first code is 0x4F + private enum InfoHdrAdjust2 : uint + { + SET_RETURNKIND = 0, // 0x00-SET_RET_KIND_MAX Set ReturnKind to value + SET_NOGCREGIONS_CNT = SET_RETURNKIND + SET_RET_KIND_MAX + 1, // 0x04 + FFFF_NOGCREGION_CNT = SET_NOGCREGIONS_CNT + SET_NOGCREGIONS_MAX + 1 // 0x09 There is a count (>SET_NOGCREGIONS_MAX) after the header encoding + }; + + public enum ReturnKinds + { + RT_Scalar = 0, + RT_Object = 1, + RT_ByRef = 2, + RT_Unset = 3, // Encoding 3 means RT_Float on X86 + RT_Scalar_Obj = RT_Object << 2 | RT_Scalar, + RT_Scalar_ByRef = RT_ByRef << 2 | RT_Scalar, + + RT_Obj_Obj = RT_Object << 2 | RT_Object, + RT_Obj_ByRef = RT_ByRef << 2 | RT_Object, + + RT_ByRef_Obj = RT_Object << 2 | RT_ByRef, + RT_ByRef_ByRef = RT_ByRef << 2 | RT_ByRef, + + RT_Illegal = 0xFF + }; + + public static InfoHdr DecodeHeader(Target target, ref TargetPointer offset, uint codeLength) + { + byte nextByte = target.Read(offset); + byte encoding = (byte)(nextByte & 0x7Fu); + + if (encoding < 0 || encoding >= INFO_HDR_TABLE.Length) + { + throw new InvalidOperationException("Table is invalid."); + } + + InfoHdr infoHdr = INFO_HDR_TABLE[encoding]; + + while ((nextByte & MORE_BYTES_TO_FOLLOW) != 0) + { + nextByte = target.Read(offset++); + encoding = (byte)(nextByte & ADJ_ENCODING_MAX); + + if (encoding < (uint)InfoHdrAdjust.NEXT_FOUR_START) + { + if (encoding < (uint)InfoHdrAdjust.SET_ARGCOUNT) + { + infoHdr.FrameSize = (byte)(encoding - (uint)InfoHdrAdjust.SET_FRAMESIZE); + } + else if (encoding < (uint)InfoHdrAdjust.SET_PROLOGSIZE) + { + infoHdr.ArgCount = (byte)(encoding - (uint)InfoHdrAdjust.SET_ARGCOUNT); + } + else if (encoding < (uint)InfoHdrAdjust.SET_EPILOGSIZE) + { + infoHdr.PrologSize = (byte)(encoding - (uint)InfoHdrAdjust.SET_PROLOGSIZE); + } + else if (encoding < (uint)InfoHdrAdjust.SET_EPILOGCNT) + { + infoHdr.EpilogSize = (byte)(encoding - (uint)InfoHdrAdjust.SET_EPILOGSIZE); + } + else if (encoding < (uint)InfoHdrAdjust.SET_UNTRACKED) + { + infoHdr.EpilogCount = (byte)((encoding - (uint)InfoHdrAdjust.SET_EPILOGCNT) / 2); + infoHdr.EpilogAtEnd = ((encoding - (uint)InfoHdrAdjust.SET_EPILOGCNT) & 1) == 1 ? true : false; + Debug.Assert(!infoHdr.EpilogAtEnd || infoHdr.EpilogCount == 1); + } + else if (encoding < (uint)InfoHdrAdjust.FIRST_FLIP) + { + infoHdr.UntrackedCount = (byte)(encoding - (uint)InfoHdrAdjust.SET_UNTRACKED); + } + else + { + switch (encoding) + { + case (byte)InfoHdrAdjust.FLIP_EDI_SAVED: + infoHdr.EdiSaved = !infoHdr.EdiSaved; + break; + case (byte)InfoHdrAdjust.FLIP_ESI_SAVED: + infoHdr.EsiSaved = !infoHdr.EsiSaved; + break; + case (byte)InfoHdrAdjust.FLIP_EBX_SAVED: + infoHdr.EbxSaved = !infoHdr.EbxSaved; + break; + case (byte)InfoHdrAdjust.FLIP_EBP_SAVED: + infoHdr.EbpSaved = !infoHdr.EbpSaved; + break; + case (byte)InfoHdrAdjust.FLIP_EBP_FRAME: + infoHdr.EbpFrame = !infoHdr.EbpFrame; + break; + case (byte)InfoHdrAdjust.FLIP_INTERRUPTIBLE: + infoHdr.Interruptible = !infoHdr.Interruptible; + break; + case (byte)InfoHdrAdjust.FLIP_DOUBLE_ALIGN: + infoHdr.DoubleAlign = !infoHdr.DoubleAlign; + break; + case (byte)InfoHdrAdjust.FLIP_SECURITY: + infoHdr.Security = !infoHdr.Security; + break; + case (byte)InfoHdrAdjust.FLIP_HANDLERS: + infoHdr.Handlers = !infoHdr.Handlers; + break; + case (byte)InfoHdrAdjust.FLIP_LOCALLOC: + infoHdr.LocalAlloc = !infoHdr.LocalAlloc; + break; + case (byte)InfoHdrAdjust.FLIP_EDITnCONTINUE: + infoHdr.EditAndContinue = !infoHdr.EditAndContinue; + break; + case (byte)InfoHdrAdjust.FLIP_VAR_PTR_TABLE_SZ: + infoHdr.VarPtrTableSize ^= HAS_VARPTR; + break; + case (byte)InfoHdrAdjust.FFFF_UNTRACKED_CNT: + infoHdr.UntrackedCount = HAS_UNTRACKED; + break; + case (byte)InfoHdrAdjust.FLIP_VARARGS: + infoHdr.VarArgs = !infoHdr.VarArgs; + break; + case (byte)InfoHdrAdjust.FLIP_PROF_CALLBACKS: + infoHdr.ProfCallbacks = !infoHdr.ProfCallbacks; + break; + case (byte)InfoHdrAdjust.FLIP_HAS_GENERICS_CONTEXT: + infoHdr.GenericsContext = !infoHdr.GenericsContext; + break; + case (byte)InfoHdrAdjust.FLIP_GENERICS_CONTEXT_IS_METHODDESC: + infoHdr.GenericsContextIsMethodDesc = !infoHdr.GenericsContextIsMethodDesc; + break; + case (byte)InfoHdrAdjust.FLIP_HAS_GS_COOKIE: + infoHdr.GsCookieOffset ^= HAS_GS_COOKIE_OFFSET; + break; + case (byte)InfoHdrAdjust.FLIP_SYNC: + infoHdr.SyncStartOffset ^= HAS_SYNC_OFFSET; + break; + case (byte)InfoHdrAdjust.FLIP_REV_PINVOKE_FRAME: + infoHdr.RevPInvokeOffset ^= HAS_REV_PINVOKE_FRAME_OFFSET; + break; + + case (byte)InfoHdrAdjust.NEXT_OPCODE: + nextByte = target.Read(offset++); + encoding = (byte)(nextByte & ADJ_ENCODING_MAX); + + // encoding here always corresponds to codes in InfoHdrAdjust2 set + if (encoding <= SET_RET_KIND_MAX) + { + infoHdr.ReturnKind = (ReturnKinds)encoding; + } + else if (encoding < (int)InfoHdrAdjust2.FFFF_NOGCREGION_CNT) + { + infoHdr.NoGCRegionCount = (uint)encoding - (uint)InfoHdrAdjust2.SET_NOGCREGIONS_CNT; + } + else if (encoding == (int)InfoHdrAdjust2.FFFF_NOGCREGION_CNT) + { + infoHdr.NoGCRegionCount = HAS_NOGCREGIONS; + } + else + { + throw new BadImageFormatException("Unexpected gcinfo header encoding"); + } + break; + default: + throw new BadImageFormatException("Unexpected gcinfo header encoding"); + } + } + } + else + { + byte lowBits; + switch (encoding >> 4) + { + case 5: + lowBits = (byte)(encoding & 0xf); + infoHdr.FrameSize <<= 4; + infoHdr.FrameSize += lowBits; + break; + case 6: + lowBits = (byte)(encoding & 0xf); + infoHdr.ArgCount <<= 4; + infoHdr.ArgCount += lowBits; + break; + case 7: + if ((encoding & 0x8) == 0) + { + lowBits = (byte)(encoding & 0x7); + infoHdr.PrologSize <<= 3; + infoHdr.PrologSize += lowBits; + } + else + { + lowBits = (byte)(encoding & 0x7); + infoHdr.EpilogSize <<= 3; + infoHdr.EpilogSize += lowBits; + } + break; + default: + throw new BadImageFormatException("Unexpected gcinfo header encoding"); + } + } + } + + if (infoHdr.UntrackedCount == HAS_UNTRACKED) + { + infoHdr.HasArgTabOffset = true; + infoHdr.UntrackedCount = target.GCDecodeUnsigned(ref offset); + } + if (infoHdr.VarPtrTableSize == HAS_VARPTR) + { + infoHdr.HasArgTabOffset = true; + infoHdr.VarPtrTableSize = target.GCDecodeUnsigned(ref offset); + } + if (infoHdr.GsCookieOffset == HAS_GS_COOKIE_OFFSET) + { + infoHdr.GsCookieOffset = target.GCDecodeUnsigned(ref offset); + } + if (infoHdr.SyncStartOffset == HAS_SYNC_OFFSET) + { + infoHdr.SyncStartOffset = target.GCDecodeUnsigned(ref offset); + infoHdr.SyndEndOffset = target.GCDecodeUnsigned(ref offset); + } + if (infoHdr.RevPInvokeOffset == HAS_REV_PINVOKE_FRAME_OFFSET) + { + infoHdr.RevPInvokeOffset = target.GCDecodeUnsigned(ref offset); + } + if (infoHdr.NoGCRegionCount == HAS_NOGCREGIONS) + { + infoHdr.HasArgTabOffset = true; + infoHdr.NoGCRegionCount = target.GCDecodeUnsigned(ref offset); + } + else if (infoHdr.NoGCRegionCount > 0) + { + infoHdr.HasArgTabOffset = true; + } + + infoHdr.Epilogs = new List(); + if (infoHdr.EpilogCount > 1 || (infoHdr.EpilogCount != 0 && !infoHdr.EpilogAtEnd)) + { + uint offs = 0; + + for (int i = 0; i < infoHdr.EpilogCount; i++) + { + offs = target.GCDecodeUDelta(ref offset, offs); + infoHdr.Epilogs.Add((int)offs); + } + } + else + { + if (infoHdr.EpilogCount != 0) + infoHdr.Epilogs.Add((int)(codeLength - infoHdr.EpilogSize)); + } + + if (infoHdr.HasArgTabOffset) + { + infoHdr.ArgTabOffset = target.GCDecodeUnsigned(ref offset); + } + + /* Sanity Checks */ + Debug.Assert(infoHdr.PrologSize + (infoHdr.EpilogCount * infoHdr.EpilogSize) <= codeLength); + Debug.Assert(infoHdr.EpilogCount == 1 || !infoHdr.EpilogAtEnd); + + Debug.Assert(infoHdr.UntrackedCount <= infoHdr.ArgCount + infoHdr.FrameSize); + + Debug.Assert(infoHdr.EbpSaved || !(infoHdr.EbpFrame || infoHdr.DoubleAlign)); + Debug.Assert(!infoHdr.EbpFrame || !infoHdr.DoubleAlign); + Debug.Assert(infoHdr.EbpFrame || !infoHdr.Security); + Debug.Assert(infoHdr.EbpFrame || !infoHdr.Handlers); + Debug.Assert(infoHdr.EbpFrame || !infoHdr.LocalAlloc); + Debug.Assert(infoHdr.EbpFrame || !infoHdr.EditAndContinue); + + return infoHdr; + } + + #endregion + #region EncodingTable + + public const uint HAS_VARPTR = 0xFFFFFFFF; + public const uint HAS_UNTRACKED = 0xFFFFFFFF; + public const uint HAS_GS_COOKIE_OFFSET = 0xFFFFFFFF; + public const uint HAS_SYNC_OFFSET = 0xFFFFFFFF; + public const uint HAS_REV_PINVOKE_FRAME_OFFSET = unchecked((uint)-2); + public const uint HAS_NOGCREGIONS = 0xFFFFFFFF; + private const uint YES = HAS_VARPTR; + + private InfoHdr( + byte prologSize, + byte epilogSize, + byte epilogCount, + byte epilogAtEnd, + byte ediSaved, + byte esiSaved, + byte ebxSaved, + byte ebpSaved, + byte ebpFrame, + byte interruptible, + byte doubleAlign, + byte security, + byte handlers, + byte localAlloc, + byte editAndContinue, + byte varArgs, + byte profCallbacks, + byte genericsContext, + byte genericsContextIsMethodDesc, + byte returnKind, + ushort argCount, + uint frameSize, + uint untrackedCount, + uint varPtrTableSize) + { + PrologSize = prologSize; + EpilogSize = epilogSize; + EpilogCount = epilogCount; + EpilogAtEnd = epilogAtEnd == 1; + EdiSaved = ediSaved == 1; + EsiSaved = esiSaved == 1; + EbxSaved = ebxSaved == 1; + EbpSaved = ebpSaved == 1; + + EbpFrame = ebpFrame == 1; + Interruptible = interruptible == 1; + DoubleAlign = doubleAlign == 1; + Security = security == 1; + Handlers = handlers == 1; + LocalAlloc = localAlloc == 1; + EditAndContinue = editAndContinue == 1; + VarArgs = varArgs == 1; + + ProfCallbacks = profCallbacks == 1; + GenericsContext = genericsContext == 1; + GenericsContextIsMethodDesc = genericsContextIsMethodDesc == 1; + ReturnKind = (ReturnKinds)returnKind; + + ArgCount = argCount; + FrameSize = frameSize; + UntrackedCount = untrackedCount; + VarPtrTableSize = varPtrTableSize; + + HasArgTabOffset = false; + ArgTabOffset = 0; + Epilogs = []; + } + + private static InfoHdr[] INFO_HDR_TABLE = + { + // Prolog size + // | + // | Epilog size + // | | + // | | Epilog count + // | | | + // | | | Epilog at end + // | | | | + // | | | | EDI saved + // | | | | | + // | | | | | ESI saved + // | | | | | | + // | | | | | | EBX saved + // | | | | | | | + // | | | | | | | EBP saved + // | | | | | | | | + // | | | | | | | | EBP-frame + // | | | | | | | | | + // | | | | | | | | | Interruptible method + // | | | | | | | | | | + // | | | | | | | | | | doubleAlign + // | | | | | | | | | | | + // | | | | | | | | | | | security flag + // | | | | | | | | | | | | + // | | | | | | | | | | | | handlers + // | | | | | | | | | | | | | + // | | | | | | | | | | | | | localloc + // | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | edit and continue + // | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | varargs + // | | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | | ProfCallbacks + // | | | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | | | genericsContext + // | | | | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | | | | genericsContextIsMethodDesc + // | | | | | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | | | | | returnKind + // | | | | | | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | | | | | | Arg count + // | | | | | | | | | | | | | | | | | | | | | Counted occurrences + // | | | | | | | | | | | | | | | | | | | | | Frame size | + // | | | | | | | | | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | | | | | | | | untrackedCnt | Header encoding + // | | | | | | | | | | | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | | | | | | | | | varPtrTable | | + // | | | | | | | | | | | | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | | | | | | | | | | gsCookieOffs | | + // | | | | | | | | | | | | | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | | | | | | | | | | | syncOffs | | + // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + // v v v v v v v v v v v v v v v v v v v v v v v v v v v v v + new( 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 1139 00 + new( 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 128738 01 + new( 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 3696 02 + new( 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 402 03 + new( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 4259 04 + new( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 ), // 3379 05 + new( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 2058 06 + new( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0 ), // 728 07 + new( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0 ), // 984 08 + new( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0 ), // 606 09 + new( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0 ), // 1110 0a + new( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 0 ), // 414 0b + new( 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 1553 0c + new( 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, YES ), // 584 0d + new( 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 2182 0e + new( 1, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 3445 0f + new( 1, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1369 10 + new( 1, 2, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 515 11 + new( 1, 2, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 21127 12 + new( 1, 2, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 3517 13 + new( 1, 2, 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 750 14 + new( 1, 4, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1876 15 + new( 1, 4, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 ), // 1665 16 + new( 1, 4, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 729 17 + new( 1, 4, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0 ), // 484 18 + new( 1, 4, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 331 19 + new( 2, 3, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 361 1a + new( 2, 3, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 964 1b + new( 2, 3, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 3713 1c + new( 2, 3, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 466 1d + new( 2, 3, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1325 1e + new( 2, 3, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 712 1f + new( 2, 3, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 588 20 + new( 2, 3, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 20542 21 + new( 2, 3, 2, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 3802 22 + new( 2, 3, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 798 23 + new( 2, 5, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1900 24 + new( 2, 5, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 385 25 + new( 2, 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1617 26 + new( 2, 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 ), // 1743 27 + new( 2, 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 909 28 + new( 2, 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0 ), // 602 29 + new( 2, 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0 ), // 352 2a + new( 2, 6, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 657 2b + new( 2, 7, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, YES ), // 1283 2c + new( 2, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 1286 2d + new( 3, 4, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1495 2e + new( 3, 4, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 1989 2f + new( 3, 4, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1154 30 + new( 3, 4, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 9300 31 + new( 3, 4, 2, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 392 32 + new( 3, 4, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 1720 33 + new( 3, 6, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1246 34 + new( 3, 6, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 800 35 + new( 3, 6, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1179 36 + new( 3, 6, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 ), // 1368 37 + new( 3, 6, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 349 38 + new( 3, 6, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0 ), // 505 39 + new( 3, 6, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 629 3a + new( 3, 8, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 2, YES ), // 365 3b + new( 4, 5, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 487 3c + new( 4, 5, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 1752 3d + new( 4, 5, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1959 3e + new( 4, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 2436 3f + new( 4, 5, 2, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 861 40 + new( 4, 7, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1459 41 + new( 4, 7, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 950 42 + new( 4, 7, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1491 43 + new( 4, 7, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 ), // 879 44 + new( 4, 7, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 408 45 + new( 5, 4, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 4870 46 + new( 5, 6, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 359 47 + new( 5, 6, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ), // 915 48 + new( 5, 6, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0 ), // 412 49 + new( 5, 6, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1288 4a + new( 5, 6, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 1591 4b + new( 5, 6, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, YES ), // 361 4c + new( 5, 6, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0 ), // 623 4d + new( 5, 8, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 ), // 1239 4e + new( 6, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 457 4f + new( 6, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 606 50 + new( 6, 4, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, YES ), // 1073 51 + new( 6, 4, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, YES ), // 508 52 + new( 6, 6, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 330 53 + new( 6, 6, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1709 54 + new( 6, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ), // 1164 55 + new( 7, 4, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 556 56 + new( 7, 5, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, YES ), // 529 57 + new( 7, 5, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, YES ), // 1423 58 + new( 7, 8, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, YES ), // 2455 59 + new( 7, 8, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ), // 956 5a + new( 7, 8, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, YES ), // 1399 5b + new( 7, 8, 2, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, YES ), // 587 5c + new( 7, 10, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 1, YES ), // 743 5d + new( 7, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0 ), // 1004 5e + new( 7, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, YES ), // 487 5f + new( 7, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0 ), // 337 60 + new( 7, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, YES ), // 361 61 + new( 8, 3, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 ), // 560 62 + new( 8, 6, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ), // 1377 63 + new( 9, 4, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 ), // 877 64 + new( 9, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ), // 3041 65 + new( 9, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, YES ), // 349 66 + new( 10, 5, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0 ), // 2061 67 + new( 10, 5, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 ), // 577 68 + new( 11, 6, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0 ), // 1195 69 + new( 12, 5, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 491 6a + new( 13, 8, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, YES ), // 627 6b + new( 13, 8, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0 ), // 1099 6c + new( 13, 10, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 1, YES ), // 488 6d + new( 14, 7, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 574 6e + new( 16, 7, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, YES ), // 1281 6f + new( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, YES ), // 1881 70 + new( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 339 71 + new( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 ), // 2594 72 + new( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0 ), // 339 73 + new( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, YES ), // 2107 74 + new( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, YES ), // 2372 75 + new( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, YES ), // 1078 76 + new( 16, 7, 2, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, YES ), // 384 77 + new( 16, 9, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 4, 1, YES ), // 1541 78 + new( 16, 9, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 4, 1, YES ), // 975 79 + new( 19, 7, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, YES ), // 546 7a + new( 24, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, YES ), // 675 7b + new( 45, 9, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 902 7c + new( 51, 7, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, YES ), // 432 7d + new( 51, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 361 7e + new( 51, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0 ), // 703 7f + }; + + #endregion +}; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/NoGcRegionTable.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/NoGcRegionTable.cs new file mode 100644 index 00000000000000..909c5b5b9f8979 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/NoGcRegionTable.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +public class NoGcRegionTable +{ + public class NoGcRegion + { + public uint Offset { get; set; } + public uint Size { get; set; } + + public NoGcRegion(uint offset, uint size) + { + Offset = offset; + Size = size; + } + + public override string ToString() + { + return $" [{Offset:04X}-{Offset+Size:04X})\n"; + } + } + + public List Regions { get; set; } + + public NoGcRegionTable(Target target, InfoHdr header, ref TargetPointer offset) + { + Regions = new List((int)header.NoGCRegionCount); + + uint count = header.NoGCRegionCount; + while (count-- > 0) + { + uint regionOffset = target.GCDecodeUnsigned(ref offset); + uint regionSize = target.GCDecodeUnsigned(ref offset); + Regions.Add(new NoGcRegion(regionOffset, regionSize)); + } + } + + public override string ToString() + { + if (Regions.Count > 0) + { + StringBuilder sb = new StringBuilder(); + + sb.AppendLine($" No GC regions:"); + foreach (NoGcRegion region in Regions) + { + sb.Append(region.ToString()); + } + + return sb.ToString(); + } + + return string.Empty; + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Context.cs new file mode 100644 index 00000000000000..f097331f2d0f84 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Context.cs @@ -0,0 +1,246 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +/// +/// X86-specific windows thread context. +/// +[StructLayout(LayoutKind.Explicit, Pack = 1)] +public struct X86Context : IPlatformContext +{ + [Flags] + public enum ContextFlagsValues : uint + { + CONTEXT_i386 = 0x00100000, + CONTEXT_CONTROL = CONTEXT_i386 | 0x1, + CONTEXT_INTEGER = CONTEXT_i386 | 0x2, + CONTEXT_SEGMENTS = CONTEXT_i386 | 0x4, + CONTEXT_FLOATING_POINT = CONTEXT_i386 | 0x8, + CONTEXT_DEBUG_REGISTERS = CONTEXT_i386 | 0x10, + CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT, + CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS, + CONTEXT_XSTATE = CONTEXT_i386 | 0x40, + } + + public readonly uint Size => 0x2cc; + public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; + + public TargetPointer StackPointer + { + readonly get => new(Esp); + set => Esp = (uint)value.Value; + } + public TargetPointer InstructionPointer + { + readonly get => new(Eip); + set => Eip = (uint)value.Value; + } + public TargetPointer FramePointer + { + readonly get => new(Ebp); + set => Ebp = (uint)value.Value; + } + + public void Unwind(Target target) + { + X86Unwinder unwinder = new(target); + unwinder.Unwind(ref this); + } + + // Control flags + + [FieldOffset(0x0)] + public uint ContextFlags; + + #region Debug registers + + [Register(RegisterType.Debug)] + [FieldOffset(0x4)] + public uint Dr0; + + [Register(RegisterType.Debug)] + [FieldOffset(0x8)] + public uint Dr1; + + [Register(RegisterType.Debug)] + [FieldOffset(0xc)] + public uint Dr2; + + [Register(RegisterType.Debug)] + [FieldOffset(0x10)] + public uint Dr3; + + [Register(RegisterType.Debug)] + [FieldOffset(0x14)] + public uint Dr6; + + [Register(RegisterType.Debug)] + [FieldOffset(0x18)] + public uint Dr7; + + #endregion + + #region Floating point registers + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x1c)] + public uint ControlWord; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x20)] + public uint StatusWord; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x24)] + public uint TagWord; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x28)] + public uint ErrorOffset; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x2c)] + public uint ErrorSelector; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x30)] + public uint DataOffset; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x34)] + public uint DataSelector; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x38)] + public Float80 ST0; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x42)] + public Float80 ST1; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x4c)] + public Float80 ST2; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x56)] + public Float80 ST3; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x60)] + public Float80 ST4; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x6a)] + public Float80 ST5; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x74)] + public Float80 ST6; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x7e)] + public Float80 ST7; + + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x88)] + public uint Cr0NpxState; + + #endregion + + #region Segment Registers + + [Register(RegisterType.Segments)] + [FieldOffset(0x8c)] + public uint Gs; + + [Register(RegisterType.Segments)] + [FieldOffset(0x90)] + public uint Fs; + + [Register(RegisterType.Segments)] + [FieldOffset(0x94)] + public uint Es; + + [Register(RegisterType.Segments)] + [FieldOffset(0x98)] + public uint Ds; + + #endregion + + #region Integer registers + + [Register(RegisterType.General)] + [FieldOffset(0x9c)] + public uint Edi; + + [Register(RegisterType.General)] + [FieldOffset(0xa0)] + public uint Esi; + + [Register(RegisterType.General)] + [FieldOffset(0xa4)] + public uint Ebx; + + [Register(RegisterType.General)] + [FieldOffset(0xa8)] + public uint Edx; + + [Register(RegisterType.General)] + [FieldOffset(0xac)] + public uint Ecx; + + [Register(RegisterType.General)] + [FieldOffset(0xb0)] + public uint Eax; + + #endregion + + #region Control registers + + [Register(RegisterType.Control | RegisterType.FramePointer)] + [FieldOffset(0xb4)] + public uint Ebp; + + [Register(RegisterType.Control | RegisterType.ProgramCounter)] + [FieldOffset(0xb8)] + public uint Eip; + + [Register(RegisterType.Segments)] + [FieldOffset(0xbc)] + public uint Cs; + + [Register(RegisterType.General)] + [FieldOffset(0xc0)] + public uint EFlags; + + [Register(RegisterType.Control | RegisterType.StackPointer)] + [FieldOffset(0xc4)] + public uint Esp; + + [Register(RegisterType.Segments)] + [FieldOffset(0xc8)] + public uint Ss; + + #endregion + + [FieldOffset(0xcc)] + public unsafe fixed byte ExtendedRegisters[512]; +} + +/// +/// Float in X86-specific windows thread context. +/// +[StructLayout(LayoutKind.Explicit, Pack = 1)] +public readonly struct Float80 +{ + [FieldOffset(0x0)] + public readonly ulong Mantissa; + + [FieldOffset(0x8)] + public readonly ushort Exponent; +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs new file mode 100644 index 00000000000000..a060dd9d8e47a5 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs @@ -0,0 +1,509 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +public class X86Unwinder(Target target) +{ + private readonly Target _target = target; + + private static readonly RegMask[] registerOrder = + [ + RegMask.EBP, // last register to be pushed + RegMask.EBX, + RegMask.ESI, + RegMask.EDI, // first register to be pushed + ]; + + #region Entrypoint + + // UnwindStackFrameX86 in src/coreclr/vm/gc_unwind_x86.inl + public bool Unwind(ref X86Context context) + { + IExecutionManager eman = _target.Contracts.ExecutionManager; + + if (eman.GetCodeBlockHandle(context.InstructionPointer.Value) is not CodeBlockHandle cbh) + { + throw new InvalidOperationException("Unwind failed, unable to find code block for the instruction pointer."); + } + + eman.GetGCInfo(cbh, out TargetPointer gcInfoAddress, out uint _); + uint relOffset = (uint)eman.GetRelativeOffset(cbh).Value; + TargetCodePointer methodStart = eman.GetStartAddress(cbh); + // TargetCodePointer funcletStart = eman.GetFuncletStartAddress(cbh); + // bool isFunclet = eman.IsFunclet(cbh); + + GCInfo gcInfo = new(_target, gcInfoAddress, relOffset); + + if (gcInfo.IsInEpilog) + { + /* First, handle the epilog */ + TargetPointer epilogBase = methodStart + (gcInfo.RelativeOffset - gcInfo.EpilogOffset); + UnwindEpilog(ref context, gcInfo, epilogBase); + } + else if (!gcInfo.Header.EbpFrame && !gcInfo.Header.DoubleAlign) + { + /* Handle ESP frames */ + UnwindEspFrame(ref context, gcInfo, methodStart); + return true; + } + else + { + // /* Now we know that we have an EBP frame */ + // if (!UnwindEbpDoubleAlignFrameEpilog( + // ref context, + // gcInfo, + // methodStart, + // funcletStart, + // isFunclet)) + // { + // return false; + // } + } + + return true; + } + + #endregion + #region Unwind Logic + + private void UnwindEpilog(ref X86Context context, GCInfo gcInfo, TargetPointer epilogBase) + { + Debug.Assert(gcInfo.IsInEpilog); + Debug.Assert(gcInfo.EpilogOffset > 0); + + if (gcInfo.Header.EbpFrame || gcInfo.Header.DoubleAlign) + { + UnwindEbpDoubleAlignFrameEpilog(ref context, gcInfo, epilogBase); + } + else + { + UnwindEspFrameEpilog(ref context, gcInfo, epilogBase); + } + + /* Now adjust stack pointer */ + context.Esp += ESPIncrementOnReturn(gcInfo); + } + + private void UnwindEbpDoubleAlignFrameEpilog(ref X86Context context, GCInfo gcInfo, TargetPointer epilogBase) + { + /* See how many instructions we have executed in the + epilog to determine which callee-saved registers + have already been popped */ + uint offset = 0; + + uint esp = context.Esp; + + bool needMovEspEbp = false; + + if (gcInfo.Header.DoubleAlign) + { + // add esp, RawStackSize + + if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) + { + esp += gcInfo.RawStackSize; + } + Debug.Assert(gcInfo.RawStackSize != 0); + offset = SKIP_ARITH_REG((int)gcInfo.RawStackSize, epilogBase, offset); + + // We also need "mov esp, ebp" after popping the callee-saved registers + needMovEspEbp = true; + } + else + { + bool needLea = false; + + if (gcInfo.Header.LocalAlloc) + { + // ESP may be variable if a localloc was actually executed. We will reset it. + // lea esp, [ebp-calleeSavedRegs] + needLea = true; + } + else if (gcInfo.SavedRegsCountExclFP == 0) + { + // We will just generate "mov esp, ebp" and be done with it. + if (gcInfo.RawStackSize != 0) + { + needMovEspEbp = true; + } + } + else if (gcInfo.RawStackSize == 0) + { + // do nothing before popping the callee-saved registers + } + else if (gcInfo.RawStackSize == _target.PointerSize) + { + // "pop ecx" will make ESP point to the callee-saved registers + if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) + { + esp += (uint)_target.PointerSize; + } + offset = SKIP_POP_REG(epilogBase, offset); + } + else + { + // We need to make ESP point to the callee-saved registers + // lea esp, [ebp-calleeSavedRegs] + + needLea = true; + } + + if (needLea) + { + // lea esp, [ebp-calleeSavedRegs] + + uint calleeSavedRegsSize = gcInfo.SavedRegsCountExclFP * (uint)_target.PointerSize; + + if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) + { + esp = context.Ebp - calleeSavedRegsSize; + } + + offset = SKIP_LEA_ESP_EBP(-(int)calleeSavedRegsSize, epilogBase, offset); + } + } + + foreach (RegMask regMask in registerOrder) + { + if (regMask == RegMask.EBP) + { + continue; // EBP is handled separately + } + + if (!gcInfo.SavedRegsMask.HasFlag(regMask)) + { + continue; + } + + if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) + { + // TODO(cdacX86): UpdateAllRegs set location?? + esp += (uint)_target.PointerSize; + } + + offset = SKIP_POP_REG(epilogBase, offset); + } + + if (needMovEspEbp) + { + if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) + esp = context.Ebp; + + offset = SKIP_MOV_REG_REG(epilogBase, offset); + } + + // Have we executed the pop EBP? + if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) + { + // TODO(cdacx86): are these equivalent? + // pContext->SetEbpLocation(PTR_DWORD(TADDR(ESP))); + // context.Ebp = _target.Read(esp); + esp += (uint)_target.PointerSize; + } + _ = SKIP_POP_REG(epilogBase, offset); + + + // TODO(cdacx86): are these equivalent? + // SetRegdisplayPCTAddr(pContext, (TADDR)ESP); + context.Eip = _target.Read(esp); + + context.Esp = esp; + } + + private void UnwindEspFrameEpilog(ref X86Context context, GCInfo gcInfo, TargetPointer epilogBase) + { + Debug.Assert(gcInfo.IsInEpilog); + Debug.Assert(!gcInfo.Header.EbpFrame && !gcInfo.Header.DoubleAlign); + Debug.Assert(gcInfo.EpilogOffset > 0); + + uint offset = 0; + uint esp = context.Esp; + + if (gcInfo.RawStackSize != 0) + { + if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) + { + /* We have NOT executed the "ADD ESP, FrameSize", + so manually adjust stack pointer */ + esp += gcInfo.RawStackSize; + } + + // We have already popped off the frame (excluding the callee-saved registers) + if (ReadByteAt(epilogBase) == X86_INSTR_POP_ECX) + { + // We may use "POP ecx" for doing "ADD ESP, 4", + // or we may not (in the case of JMP epilogs) + Debug.Assert(gcInfo.RawStackSize == _target.PointerSize); + offset = SKIP_POP_REG(epilogBase, offset); + } + else + { + // "add esp, rawStkSize" + offset = SKIP_ARITH_REG((int)gcInfo.RawStackSize, epilogBase, offset); + } + } + + /* Remaining callee-saved regs are at ESP. Need to update + regsMask as well to exclude registers which have already been popped. */ + foreach (RegMask regMask in registerOrder) + { + if (!gcInfo.SavedRegsMask.HasFlag(regMask)) + continue; + + if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) + { + /* We have NOT yet popped off the register. + Get the value from the stack if needed */ + // TODO(cdacX86): UpdateAllRegs set location?? + esp += (uint)_target.PointerSize; + } + + offset = SKIP_POP_REG(epilogBase, offset); + } + + //CEE_JMP generates an epilog similar to a normal CEE_RET epilog except for the last instruction + Debug.Assert( + CheckInstrBytePattern((byte)(ReadByteAt(epilogBase + offset) & X86_INSTR_RET), X86_INSTR_RET, ReadByteAt(epilogBase + offset)) //ret + || CheckInstrBytePattern(ReadByteAt(epilogBase + offset), X86_INSTR_JMP_NEAR_REL32, ReadByteAt(epilogBase + offset)) //jmp ret32 + || CheckInstrWord(ReadShortAt(epilogBase + offset), X86_INSTR_w_JMP_FAR_IND_IMM)); //jmp [addr32] + + /* Finally we can set pPC */ + // TODO(cdacx86): are these equivalent? + // SetRegdisplayPCTAddr(pContext, (TADDR)ESP); + context.Eip = _target.Read(esp); + + context.Esp = esp; + } + + private void UnwindEspFrame(ref X86Context context, GCInfo gcInfo, TargetCodePointer methodStart) + { + Debug.Assert(!gcInfo.Header.EbpFrame && !gcInfo.Header.DoubleAlign); + Debug.Assert(!gcInfo.IsInEpilog); + + Console.WriteLine(methodStart); + + uint esp = context.Esp; + + if (gcInfo.IsInProlog) + { + if (gcInfo.PrologOffset != 0) // Do nothing for the very start of the method + { + // TODO(cdacx86): + // UnwindEspFrameProlog(pContext, info, methodStart, flags); + esp = context.Esp; + } + } + else + { + /* We are past the prolog, ESP has been set above */ + + // Handle arguments pushed to the stack + + // TODO(cdacx86): calculate pushed arg size. This involves reading the ArgRegTable from the GCInfo. + + esp += gcInfo.RawStackSize; + + foreach (RegMask regMask in registerOrder) + { + if (!gcInfo.SavedRegsMask.HasFlag(regMask)) + continue; + + // TODO(cdacx86): UpdateAllRegs set location?? + // SetLocation(pContext, i - 1, PTR_DWORD((TADDR)ESP)); + + // Pop the callee-saved registers + esp += (uint)_target.PointerSize; + } + } + + /* we can now set the (address of the) return address */ + // TODO(cdacx86): are these equivalent? + // SetRegdisplayPCTAddr(pContext, (TADDR)ESP); + context.Eip = _target.Read(esp); + + context.Esp = esp + ESPIncrementOnReturn(gcInfo); + } + + // private bool UnwindEbpDoubleAlignFrameEpilog( + // ref X86Context context, + // GCInfo gcInfo, + // TargetCodePointer methodStart, + // TargetCodePointer funcletStart, + // bool isFunclet) + // { + // Debug.Assert(gcInfo.Header.EbpFrame || gcInfo.Header.DoubleAlign); + + // uint curEsp = context.Esp; + // uint curEbp = context.Ebp; + + // /* First check if we are in a filter (which is obviously after the prolog) */ + // if (gcInfo.Header.Handlers && !gcInfo.IsInProlog) + // { + // TargetPointer baseSP; + + // if (isFunclet) + // { + // baseSP = curEsp; + // // Set baseSP as initial SP + + // // baseSP += GetPushedArgSize(info, table, curOffs); + // } + // else + // { + // baseSP = methodStart; + // } + // } + + // return false; + // } + + #endregion + #region Helper Methods + + // Use this to check if the instruction at offset "walkOffset" has already + // been executed + // "actualHaltOffset" is the offset when the code was suspended + // It is assumed that there is linear control flow from offset 0 to "actualHaltOffset". + // + // This has been factored out just so that the intent of the comparison + // is clear (compared to the opposite intent) + private static bool InstructionAlreadyExecuted(uint walkOffset, uint actualHaltOffset) + { + return walkOffset < actualHaltOffset; + } + + private uint ESPIncrementOnReturn(GCInfo gcInfo) + { + + uint stackParameterSize = gcInfo.Header.VarArgs ? 0 // varargs are caller-popped + : gcInfo.Header.ArgCount * (uint)_target.PointerSize; + return (uint)_target.PointerSize /* return address size */ + stackParameterSize; + } + + // skips past a "arith REG, IMM" + private uint SKIP_ARITH_REG(int val, TargetPointer baseAddress, uint offset) + { + uint delta = 0; + if (val != 0) + { + // Confirm that arith instruction is at the correct place + Debug.Assert(CheckInstrBytePattern((byte)(ReadByteAt(baseAddress + offset) & 0xFD), 0x81, ReadByteAt(baseAddress + offset))); + Debug.Assert(CheckInstrBytePattern((byte)(ReadByteAt(baseAddress + offset + 1) & 0xC0), 0xC0, ReadByteAt(baseAddress + offset + 1))); + + // only use DWORD form if needed + Debug.Assert(((ReadByteAt(baseAddress + offset) & 2) != 0 == CAN_COMPRESS(val)) || IsMarkerInstr(ReadByteAt(baseAddress + offset))); + + delta = 2u + (CAN_COMPRESS(val) ? 1u : 4u); + } + return offset + delta; + } + + private uint SKIP_POP_REG(TargetPointer baseAddress, uint offset) + { + // Confirm it is a pop instruction + Debug.Assert(CheckInstrBytePattern((byte)(ReadByteAt(baseAddress + offset) & 0xF8), 0x58, ReadByteAt(baseAddress + offset))); + + return offset + 1; + } + + private uint SKIP_LEA_ESP_EBP(int val, TargetPointer baseAddress, uint offset) + { + // Confirm it is the right instruction + // Note that only the first byte may have been stomped on by IsMarkerInstr() + // So we can check the second byte directly + Debug.Assert( + (CheckInstrWord(ReadShortAt(baseAddress), X86_INSTR_w_LEA_ESP_EBP_BYTE_OFFSET) && + (val == ReadSByteAt(baseAddress + 2)) && + CAN_COMPRESS(val)) + || + (CheckInstrWord(ReadShortAt(baseAddress), X86_INSTR_w_LEA_ESP_EBP_DWORD_OFFSET) && + (val == ReadIntAt(baseAddress + 2)) && + !CAN_COMPRESS(val)) + ); + + uint delta = 2u + (CAN_COMPRESS(val) ? 1u : 4u); + return offset + delta; + } + + private uint SKIP_MOV_REG_REG(TargetPointer baseAddress, uint offset) + { + // Confirm it is a move instruction + // Note that only the first byte may have been stomped on by IsMarkerInstr() + // So we can check the second byte directly + Debug.Assert( + CheckInstrBytePattern((byte)(ReadByteAt(baseAddress + offset) & 0xFD), 0x89, ReadByteAt(baseAddress + offset)) + && + (ReadByteAt(baseAddress + offset) & 0xC0) == 0xC0 + ); + return offset + 2; + } + + private static bool CAN_COMPRESS(int val) + { + return ((byte)val) == val; + } + + #endregion + + #region Verification Helpers + + private const byte X86_INSTR_INT3 = 0xCC; // int3 + private const byte X86_INSTR_POP_ECX = 0x59; // pop ecx + private const byte X86_INSTR_RET = 0xC2; // ret imm16 + private const byte X86_INSTR_JMP_NEAR_REL32 = 0xE9; // near jmp rel32 + private const ushort X86_INSTR_w_JMP_FAR_IND_IMM = 0x25FF; // far jmp [addr32] + + private const ushort X86_INSTR_w_LEA_ESP_EBP_BYTE_OFFSET = 0x658d; // lea esp, [ebp-bOffset] + private const ushort X86_INSTR_w_LEA_ESP_EBP_DWORD_OFFSET = 0xa58d; // lea esp, [ebp-dwOffset] + + + /* Similar to CheckInstrByte(). Use this to check a masked opcode (ignoring + optional bits in the opcode encoding). + valPattern is the masked out value. + expectedPattern is the mask value we expect. + val is the actual instruction opcode */ + private static bool CheckInstrBytePattern(byte valPattern, byte expectedPattern, byte val) + { + Debug.Assert((valPattern & val) == valPattern); + + return (valPattern == expectedPattern) || IsMarkerInstr(val); + } + + /* Similar to CheckInstrByte() */ + private static bool CheckInstrWord(ushort val, ushort expectedValue) + { + return (val == expectedValue) || IsMarkerInstr((byte)(val & 0xFF)); + } + + private static bool IsMarkerInstr(byte val) + { + return val == X86_INSTR_INT3; + } + + private sbyte ReadSByteAt(TargetPointer address) + { + return _target.Read(address); + } + + private byte ReadByteAt(TargetPointer address) + { + return _target.Read(address); + } + + private ushort ReadShortAt(TargetPointer address) + { + return _target.Read(address); + } + + private int ReadIntAt(TargetPointer address) + { + return _target.Read(address); + } + + #endregion +} diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index a6360422ce5837..ba2c72082d6796 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -11,6 +11,7 @@ using Microsoft.Diagnostics.DataContractReader.Contracts; using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; +using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; namespace Microsoft.Diagnostics.DataContractReader.Legacy; @@ -804,6 +805,16 @@ int ISOSDacInterface.GetMethodDescPtrFromIP(ulong ip, ulong* ppMD) { TargetPointer methodDescAddr = executionManager.GetMethodDesc(codeHandle); + TargetCodePointer funcletStart = executionManager.GetFuncletStartAddress(codeHandle); + bool isFunclet = executionManager.IsFunclet(codeHandle); + TargetPointer unwindInfo = executionManager.GetUnwindInfo(codeHandle); + executionManager.GetGCInfo(codeHandle, out TargetPointer gcInfoPtr, out uint gcVersion); + TargetNUInt relOffset = executionManager.GetRelativeOffset(codeHandle); + GCInfo gcInfo = new(_target, gcInfoPtr, (uint)relOffset.Value); + Console.WriteLine(gcInfo.Transitions); + Console.WriteLine($"IP: {ip:x}, MethodDesc: {methodDescAddr.Value:x}, FuncletStart: {funcletStart.Value:x}, IsFunclet: {isFunclet}, UnwindInfo: {unwindInfo.Value:x}"); + Console.WriteLine($"GCInfo: {gcInfoPtr.Value:x}, GCVersion: {gcVersion}"); + try { // Runs validation of MethodDesc From 6829ddaf8b0367ed27ffcad7128497f32dabdf3f Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 21 May 2025 20:50:17 -0400 Subject: [PATCH 05/56] wip working PushedArgDepth --- .../StackWalk/Context/X86/GCInfoDecoder.cs | 69 ++++++++++++++++++- .../StackWalk/Context/X86/GCTransition.cs | 25 ++++++- .../Legacy/SOSDacImpl.cs | 13 ++++ 3 files changed, 104 insertions(+), 3 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs index 5fd9d44d2e19ce..6638b6ceef5236 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.Linq; namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; @@ -55,6 +57,9 @@ public record GCInfo public GcSlotTable SlotTable { get; set; } = null!; public Dictionary> Transitions { get; set; } = []; + // Number of bytes of stack space that has been pushed for arguments at the current RelativeOffset. + public uint PushedArgStackDepth { get; set; } + public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) { _target = target; @@ -126,7 +131,7 @@ public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) SlotTable = new GcSlotTable(target, Header, ref offset); // Verify the argument table offset is consistent - Debug.Assert(offset.Value == gcInfoAddress.Value + infoHdrSize + Header.ArgTabOffset); + Debug.Assert(!Header.HasArgTabOffset || offset.Value == gcInfoAddress.Value + infoHdrSize + Header.ArgTabOffset); Transitions = new Dictionary>(); if (Header.Interruptible) { @@ -140,6 +145,63 @@ public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) { GetTransitionsNoEbp(ref offset); } + + CalculateDepth(); + } + + private void CalculateDepth() + { + using StreamWriter outputFile = new StreamWriter("C:\\Users\\maxcharlamb\\OneDrive - Microsoft\\Desktop\\out.txt", true); + + int depth = 0; + outputFile.WriteLine($"depth: {depth}"); + foreach (int offset in Transitions.Keys.OrderBy(i => i)) + { + if (offset > RelativeOffset) + break; // calculate only to current offset + outputFile.WriteLine($"CodeOffset: {offset:x8}"); + foreach (BaseGcTransition gcTransition in Transitions[offset]) + { + outputFile.WriteLine(gcTransition); + + switch (gcTransition) + { + case GcTransitionRegister gcTransitionRegister: + if (gcTransitionRegister.IsLive == Action.PUSH) + { + depth += gcTransitionRegister.PushCountOrPopSize; + } + else if (gcTransitionRegister.IsLive == Action.POP) + { + depth -= gcTransitionRegister.PushCountOrPopSize; + } + break; + case StackDepthTransition stackDepthTransition: + depth += stackDepthTransition.StackDepthChange; + break; + case GcTransitionPointer gcTransitionPointer: + if (gcTransitionPointer.Act == Action.PUSH) + { + // FEATURE_EH_FUNCLETS implies fullArgInfo + // when there is fullArgInfo, the current depth is incremented by the number of pushed arguments + depth++; + } + else if (gcTransitionPointer.Act == Action.POP) + { + depth -= (int)gcTransitionPointer.ArgOffset; + } + break; + case IPtrMask: + case GcTransitionCall: + break; + default: + throw new InvalidOperationException("Unsupported gc transition type"); + } + } + outputFile.WriteLine($"depth: {depth}"); + } + + PushedArgStackDepth = (uint)(depth * _target.PointerSize); } private void AddNewTransition(BaseGcTransition transition) @@ -290,6 +352,7 @@ private void GetTransitionsFullyInterruptible(ref TargetPointer offset) break; case 0xF9: argOffs = _target.GCDecodeUnsigned(ref offset); + AddNewTransition(new StackDepthTransition((int)curOffs, (int)argOffs)); argCnt += argOffs; break; default: @@ -559,6 +622,7 @@ private void GetTransitionsNoEbp(ref TargetPointer offset) CallPattern.DecodeCallPattern((val & 0x7f), out callArgCnt, out callRegMask, out callPndMask, out lastSkip); curOffs += lastSkip; SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); break; case 5: @@ -573,6 +637,7 @@ private void GetTransitionsNoEbp(ref TargetPointer offset) lastSkip = CallPattern.callCommonDelta[val >> 6]; curOffs += lastSkip; SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); break; case 6: // @@ -583,6 +648,7 @@ private void GetTransitionsNoEbp(ref TargetPointer offset) callArgCnt = _target.GCDecodeUnsigned(ref offset); callPndMask = _target.GCDecodeUnsigned(ref offset); SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); break; case 7: switch (val & 0x0C) @@ -612,6 +678,7 @@ private void GetTransitionsNoEbp(ref TargetPointer offset) offset += 4; callPndTab = true; SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); break; case 0x0C: return; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCTransition.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCTransition.cs index a158924311f910..f647c4bb301bff 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCTransition.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCTransition.cs @@ -74,7 +74,7 @@ public class GcTransitionRegister : BaseGcTransition public GcTransitionRegister() { } - public GcTransitionRegister(int codeOffset, RegMask reg, Action isLive, bool isThis = false, bool iptr = false, int pushCountOrPopSize = -1) + public GcTransitionRegister(int codeOffset, RegMask reg, Action isLive, bool isThis = false, bool iptr = false, int pushCountOrPopSize = 1) : base(codeOffset) { Register = reg; @@ -97,7 +97,7 @@ public override string ToString() else { sb.Append((IsLive == Action.PUSH ? "push" : "pop") + $" {Register}"); - if (PushCountOrPopSize != -1) + if (PushCountOrPopSize != 1) sb.Append($" {PushCountOrPopSize}"); } @@ -289,3 +289,24 @@ public override string ToString() return sb.ToString(); } } + +public class StackDepthTransition : BaseGcTransition +{ + public int StackDepthChange { get; set; } + + public StackDepthTransition(int codeOffset) + : base(codeOffset) + { + } + + public StackDepthTransition(int codeOffset, int stackDepthChange) + : base(codeOffset) + { + StackDepthChange = stackDepthChange; + } + + public override string ToString() + { + return $"stack depth delta: {StackDepthChange}"; + } +} diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index ba2c72082d6796..64787725781241 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -811,6 +812,18 @@ int ISOSDacInterface.GetMethodDescPtrFromIP(ulong ip, ulong* ppMD) executionManager.GetGCInfo(codeHandle, out TargetPointer gcInfoPtr, out uint gcVersion); TargetNUInt relOffset = executionManager.GetRelativeOffset(codeHandle); GCInfo gcInfo = new(_target, gcInfoPtr, (uint)relOffset.Value); + + using (StreamWriter outputFile = new StreamWriter("C:\\Users\\maxcharlamb\\OneDrive - Microsoft\\Desktop\\out.txt", true)) + { + foreach (int offset in gcInfo.Transitions.Keys) + { + // outputFile.WriteLine($"CodeOffset: {offset:x8}"); + foreach (BaseGcTransition gcTransition in gcInfo.Transitions[offset]) + { + // outputFile.WriteLine(gcTransition); + } + } + } Console.WriteLine(gcInfo.Transitions); Console.WriteLine($"IP: {ip:x}, MethodDesc: {methodDescAddr.Value:x}, FuncletStart: {funcletStart.Value:x}, IsFunclet: {isFunclet}, UnwindInfo: {unwindInfo.Value:x}"); Console.WriteLine($"GCInfo: {gcInfoPtr.Value:x}, GCVersion: {gcVersion}"); From f71b823482c34203034e1745f773f0536bf7319f Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Thu, 22 May 2025 14:36:46 -0400 Subject: [PATCH 06/56] finish the bones of x86 unwinder --- .../StackWalk/Context/X86/GCInfoDecoder.cs | 4 +- .../StackWalk/Context/X86/X86Unwinder.cs | 535 ++++++++++++++++-- 2 files changed, 487 insertions(+), 52 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs index 6638b6ceef5236..2d8eb8c7c59938 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs @@ -58,7 +58,7 @@ public record GCInfo public Dictionary> Transitions { get; set; } = []; // Number of bytes of stack space that has been pushed for arguments at the current RelativeOffset. - public uint PushedArgStackDepth { get; set; } + public uint PushedArgSize { get; set; } public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) { @@ -201,7 +201,7 @@ private void CalculateDepth() outputFile.WriteLine($"depth: {depth}"); } - PushedArgStackDepth = (uint)(depth * _target.PointerSize); + PushedArgSize = (uint)(depth * _target.PointerSize); } private void AddNewTransition(BaseGcTransition transition) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs index a060dd9d8e47a5..95a5878e89a43f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs @@ -3,13 +3,15 @@ using System; using System.Diagnostics; -using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; +using System.Linq; namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; public class X86Unwinder(Target target) { private readonly Target _target = target; + private readonly bool _updateAllRegs = true; + private readonly bool _unixX86ABI = target.Contracts.RuntimeInfo.GetTargetOperatingSystem() == RuntimeInfoOperatingSystem.Unix; private static readonly RegMask[] registerOrder = [ @@ -53,16 +55,16 @@ public bool Unwind(ref X86Context context) } else { - // /* Now we know that we have an EBP frame */ - // if (!UnwindEbpDoubleAlignFrameEpilog( - // ref context, - // gcInfo, - // methodStart, - // funcletStart, - // isFunclet)) - // { - // return false; - // } + /* Now we know that we have an EBP frame */ + if (!UnwindEbpDoubleAlignFrame( + ref context, + gcInfo, + methodStart, + funcletStart, + isFunclet)) + { + return false; + } } return true; @@ -293,8 +295,7 @@ private void UnwindEspFrame(ref X86Context context, GCInfo gcInfo, TargetCodePoi { if (gcInfo.PrologOffset != 0) // Do nothing for the very start of the method { - // TODO(cdacx86): - // UnwindEspFrameProlog(pContext, info, methodStart, flags); + UnwindEspFrameProlog(ref context, gcInfo, methodStart); esp = context.Esp; } } @@ -302,10 +303,7 @@ private void UnwindEspFrame(ref X86Context context, GCInfo gcInfo, TargetCodePoi { /* We are past the prolog, ESP has been set above */ - // Handle arguments pushed to the stack - - // TODO(cdacx86): calculate pushed arg size. This involves reading the ArgRegTable from the GCInfo. - + esp += gcInfo.PushedArgSize; esp += gcInfo.RawStackSize; foreach (RegMask regMask in registerOrder) @@ -329,38 +327,270 @@ private void UnwindEspFrame(ref X86Context context, GCInfo gcInfo, TargetCodePoi context.Esp = esp + ESPIncrementOnReturn(gcInfo); } - // private bool UnwindEbpDoubleAlignFrameEpilog( - // ref X86Context context, - // GCInfo gcInfo, - // TargetCodePointer methodStart, - // TargetCodePointer funcletStart, - // bool isFunclet) - // { - // Debug.Assert(gcInfo.Header.EbpFrame || gcInfo.Header.DoubleAlign); - - // uint curEsp = context.Esp; - // uint curEbp = context.Ebp; - - // /* First check if we are in a filter (which is obviously after the prolog) */ - // if (gcInfo.Header.Handlers && !gcInfo.IsInProlog) - // { - // TargetPointer baseSP; - - // if (isFunclet) - // { - // baseSP = curEsp; - // // Set baseSP as initial SP - - // // baseSP += GetPushedArgSize(info, table, curOffs); - // } - // else - // { - // baseSP = methodStart; - // } - // } - - // return false; - // } + private void UnwindEspFrameProlog(ref X86Context context, GCInfo gcInfo, TargetCodePointer methodStart) + { + Debug.Assert(gcInfo.IsInProlog); + Debug.Assert(!gcInfo.Header.EbpFrame && !gcInfo.Header.DoubleAlign); + + uint offset = 0; + + // TODO(cdacX86): Why only check for methods with JitHalt in debug builds? + + uint curOffs = gcInfo.PrologOffset; + uint esp = context.Esp; + + RegMask regsMask = RegMask.NONE; + TargetPointer savedRegPtr = esp; + + // Find out how many callee-saved regs have already been pushed + foreach (RegMask regMask in registerOrder) + { + if (!gcInfo.SavedRegsMask.HasFlag(regMask)) + continue; + + if (InstructionAlreadyExecuted(offset, curOffs)) + { + esp += (uint)_target.PointerSize; + regsMask |= regMask; + } + + offset = SKIP_PUSH_REG(methodStart.Value, offset); + } + + if (gcInfo.RawStackSize != 0) + { + offset = SKIP_ALLOC_FRAME((int)gcInfo.RawStackSize, methodStart.Value, offset); + + // Note that this assumes that only the last instruction in SKIP_ALLOC_FRAME + // actually updates ESP + if (InstructionAlreadyExecuted(offset, curOffs + 1)) + { + savedRegPtr += gcInfo.RawStackSize; + esp += gcInfo.RawStackSize; + } + } + + // + // Stack probe checks here + // + + // Poison the value, we don't set it properly at the end of the prolog +#if DEBUG + offset = 0xCCCCCCCC; +#endif + + // Always restore EBP + if (regsMask.HasFlag(RegMask.EBP)) + { + context.Ebp = _target.Read(savedRegPtr); + savedRegPtr += (uint)_target.PointerSize; + } + + if (_updateAllRegs) + { + if (regsMask.HasFlag(RegMask.EBX)) + { + context.Ebx = _target.Read(savedRegPtr); + savedRegPtr += (uint)_target.PointerSize; + } + if (regsMask.HasFlag(RegMask.ESI)) + { + context.Esi = _target.Read(savedRegPtr); + savedRegPtr += (uint)_target.PointerSize; + } + if (regsMask.HasFlag(RegMask.EDI)) + { + context.Edi = _target.Read(savedRegPtr); + savedRegPtr += (uint)_target.PointerSize; + } + + // TODO(cdacX86): Do we handle trashing anything here? + // TRASH_CALLEE_UNSAVED_REGS(pContext); + } + + // NOTE: + // THIS IS ONLY TRUE IF PROLOGSIZE DOES NOT INCLUDE REG-VAR INITIALIZATION !!!! + // + /* there is (potentially) only one additional + instruction in the prolog, (push ebp) + but if we would have been passed that instruction, + info->prologOffs would be hdrInfo::NOT_IN_PROLOG! + */ + // TODO(cdacX86): Does this check matter? It was disabled in the runtime + //_ASSERTE(offset == info->prologOffs); + + context.Esp = esp; + } + + private bool UnwindEbpDoubleAlignFrame( + ref X86Context context, + GCInfo gcInfo, + TargetCodePointer methodStart, + TargetCodePointer funcletStart, + bool isFunclet) + { + Debug.Assert(gcInfo.Header.EbpFrame || gcInfo.Header.DoubleAlign); + + uint curEsp = context.Esp; + uint curEbp = context.Ebp; + + /* First check if we are in a filter (which is obviously after the prolog) */ + if (gcInfo.Header.Handlers && !gcInfo.IsInProlog) + { + TargetPointer baseSP; + + if (isFunclet) + { + baseSP = curEsp; + // Set baseSP as initial SP + baseSP += gcInfo.PushedArgSize; + + if (_unixX86ABI) + { + // 16-byte stack alignment padding (allocated in genFuncletProlog) + // Current funclet frame layout (see CodeGen::genFuncletProlog() and genFuncletEpilog()): + // prolog: sub esp, 12 + // epilog: add esp, 12 + // ret + // SP alignment padding should be added for all instructions except the first one and the last one. + // Epilog may not exist (unreachable), so we need to check the instruction code. + if (funcletStart != methodStart + gcInfo.RelativeOffset && ReadByteAt(methodStart + gcInfo.RelativeOffset) != X86_INSTR_RETN) + baseSP += 12; + } + + context.Eip = (uint)_target.ReadPointer(baseSP); + context.Esp = (uint)baseSP + (uint)_target.PointerSize; + return true; + } + + // TODO(cdacX86): Handle filter frames + } + + // + // Prolog of an EBP method + // + + if (gcInfo.IsInProlog) + { + UnwindEbpDoubleAlignFrameProlog(ref context, gcInfo, methodStart.Value); + + /* Now adjust stack pointer. */ + + context.Esp += ESPIncrementOnReturn(gcInfo); + return true; + } + + if (_updateAllRegs) + { + // Get to the first callee-saved register + TargetPointer pSavedRegs = curEbp; + if (gcInfo.Header.DoubleAlign && (curEbp & 0x04) != 0) + pSavedRegs -= (uint)_target.PointerSize; + + foreach (RegMask regMask in registerOrder.Reverse()) + { + if (regMask == RegMask.EBP) continue; + + if (!gcInfo.SavedRegsMask.HasFlag(regMask)) continue; + + // TODO(cdacX86): set register locations + Console.WriteLine(pSavedRegs); + } + } + + /* The caller's ESP will be equal to EBP + retAddrSize + argSize. */ + context.Esp = curEbp + (uint)_target.PointerSize + ESPIncrementOnReturn(gcInfo); + + /* The caller's saved EIP is right after our EBP */ + context.Eip = (uint)_target.ReadPointer(curEbp + (uint)_target.PointerSize); + + /* The caller's saved EBP is pointed to by our EBP */ + context.Ebp = (uint)_target.ReadPointer(curEbp); + return true; + } + + private void UnwindEbpDoubleAlignFrameProlog(ref X86Context context, GCInfo gcInfo, TargetPointer methodStart) + { + Debug.Assert(gcInfo.IsInProlog); + Debug.Assert(gcInfo.Header.EbpFrame || gcInfo.Header.DoubleAlign); + + uint offset = 0; + + // TODO(cdacX86): Why only check for methods with JitHalt in debug builds? + + /* Check for the case where EBP has not been updated yet. */ + uint curOffs = gcInfo.PrologOffset; + + // If we have still not executed "push ebp; mov ebp, esp", then we need to + // report the frame relative to ESP + + if (!InstructionAlreadyExecuted(offset + 1, curOffs)) + { + Debug.Assert(CheckInstrByte(ReadByteAt(methodStart + offset), X86_INSTR_PUSH_EBP) || + CheckInstrWord(ReadShortAt(methodStart + offset), X86_INSTR_W_MOV_EBP_ESP) || + CheckInstrByte(ReadByteAt(methodStart + offset), X86_INSTR_JMP_NEAR_REL32)); // a rejit jmp-stamp + + /* If we're past the "push ebp", adjust ESP to pop EBP off */ + if (curOffs == (offset + 1)) + context.Esp += (uint)_target.PointerSize; + + /* Stack pointer points to return address */ + context.Eip = (uint)_target.ReadPointer(context.Esp); + + /* EBP and callee-saved registers still have the correct value */ + return; + } + + // We are atleast after the "push ebp; mov ebp, esp" + offset = SKIP_MOV_REG_REG(methodStart, SKIP_PUSH_REG(methodStart, offset)); + + /* At this point, EBP has been set up. The caller's ESP and the return value + can be determined using EBP. Since we are still in the prolog, + we need to know our exact location to determine the callee-saved registers */ + uint curEBP = context.Ebp; + + if (_updateAllRegs) + { + TargetPointer pSavedRegs = curEBP; + + /* make sure that we align ESP just like the method's prolog did */ + if (gcInfo.Header.DoubleAlign) + { + // "and esp,-8" + offset = SKIP_ARITH_REG(-8, methodStart, offset); + if ((curEBP & 0x04) != 0) pSavedRegs--; + } + + /* Increment "offset" in steps to see which callee-saved + registers have been pushed already */ + + foreach (RegMask regMask in registerOrder) + { + if (regMask == RegMask.EBP) continue; + + if (!gcInfo.SavedRegsMask.HasFlag(regMask)) continue; + + if (InstructionAlreadyExecuted(offset, curOffs)) + { + // TODO(cdacx86): UpdateAllRegs set location?? + // SetLocation(pContext, i, PTR_DWORD(--pSavedRegs)); + Console.WriteLine(pSavedRegs); + } + + offset = SKIP_PUSH_REG(methodStart, offset); + } + + // TODO(cdacX86): Do we handle trashing anything here? + // TRASH_CALLEE_UNSAVED_REGS(pContext); + } + + /* The caller's saved EBP is pointed to by our EBP */ + context.Ebp = (uint)_target.ReadPointer(curEBP); + context.Esp = (uint)_target.ReadPointer(curEBP + (uint)_target.PointerSize); + + /* Stack pointer points to return address */ + context.Eip = (uint)_target.ReadPointer(context.Esp); + } #endregion #region Helper Methods @@ -411,6 +641,13 @@ private uint SKIP_POP_REG(TargetPointer baseAddress, uint offset) return offset + 1; } + private uint SKIP_PUSH_REG(TargetPointer baseAddress, uint offset) + { + // Confirm it is a push instruction + Debug.Assert(CheckInstrBytePattern((byte)(ReadByteAt(baseAddress + offset) & 0xF8), 0x50, ReadByteAt(baseAddress + offset))); + return offset + 1; + } + private uint SKIP_LEA_ESP_EBP(int val, TargetPointer baseAddress, uint offset) { // Confirm it is the right instruction @@ -443,6 +680,185 @@ private uint SKIP_MOV_REG_REG(TargetPointer baseAddress, uint offset) return offset + 2; } + private uint SKIP_ALLOC_FRAME(int size, TargetPointer baseAddress, uint offset) + { + Debug.Assert(size != 0); + + if (size == _target.PointerSize) + { + // JIT emits "push eax" instead of "sub esp,4" + return SKIP_PUSH_REG(baseAddress, offset); + } + + const int STACK_PROBE_PAGE_SIZE_BYTES = 4096; + const int STACK_PROBE_BOUNDARY_THRESHOLD_BYTES = 1024; + + int lastProbedLocToFinalSp = size; + + if (size < STACK_PROBE_PAGE_SIZE_BYTES) + { + // sub esp, size + offset = SKIP_ARITH_REG(size, baseAddress, offset); + } + else + { + ushort wOpcode = ReadShortAt(baseAddress + offset); + + if (CheckInstrWord(wOpcode, X86_INSTR_w_TEST_ESP_DWORD_OFFSET_EAX)) + { + // TODO(cdacX86): This whole section is probably irrelevant for modern versions. + + // In .NET 5.0 and earlier for frames that have size smaller than 0x3000 bytes + // JIT emits one or two 'test eax, [esp-dwOffset]' instructions before adjusting the stack pointer. + Debug.Assert(size < 0x3000); + + // test eax, [esp-0x1000] + offset += 7; + lastProbedLocToFinalSp -= 0x1000; + + if (size >= 0x2000) + { + Debug.Assert(CheckInstrWord(ReadShortAt(baseAddress + offset), X86_INSTR_w_TEST_ESP_DWORD_OFFSET_EAX)); + + //test eax, [esp-0x2000] + offset += 7; + lastProbedLocToFinalSp -= 0x1000; + } + + // sub esp, size + offset = SKIP_ARITH_REG(size, baseAddress, offset); + } + else + { + bool pushedStubParam = false; + + if (CheckInstrByte(ReadByteAt(baseAddress + offset), X86_INSTR_PUSH_EAX)) + { + // push eax + offset = SKIP_PUSH_REG(baseAddress, offset); + pushedStubParam = true; + } + + if (CheckInstrByte(ReadByteAt(baseAddress + offset), X86_INSTR_XOR)) + { + // TODO(cdacX86): This whole section is probably irrelevant for modern versions. + + // In .NET Core 3.1 and earlier for frames that have size greater than or equal to 0x3000 bytes + // JIT emits the following loop. + Debug.Assert(size >= 0x3000); + + offset += 2; + // xor eax, eax 2 + // [nop] 0-3 + // loop: + // test [esp + eax], eax 3 + // sub eax, 0x1000 5 + // cmp eax, -size 5 + // jge loop 2 + + // R2R images that support ReJIT may have extra nops we need to skip over. + while (offset < 5) + { + if (CheckInstrByte(ReadByteAt(baseAddress + offset), X86_INSTR_NOP)) + { + offset++; + } + else + { + break; + } + } + + offset += 15; + + if (pushedStubParam) + { + // pop eax + offset = SKIP_POP_REG(baseAddress, offset); + } + + // sub esp, size + return SKIP_ARITH_REG(size, baseAddress, offset); + } + else + { + // In .NET 5.0 and later JIT emits a call to JIT_StackProbe helper. + + if (pushedStubParam) + { + // lea eax, [esp-size+4] + offset = SKIP_LEA_EAX_ESP(-size + 4, baseAddress, offset); + // call JIT_StackProbe + offset = SKIP_HELPER_CALL(baseAddress, offset); + // pop eax + offset = SKIP_POP_REG(baseAddress, offset); + // sub esp, size + return SKIP_ARITH_REG(size, baseAddress, offset); + } + else + { + // lea eax, [esp-size] + offset = SKIP_LEA_EAX_ESP(-size, baseAddress, offset); + // call JIT_StackProbe + offset = SKIP_HELPER_CALL(baseAddress, offset); + // mov esp, eax + return SKIP_MOV_REG_REG(baseAddress, offset); + } + } + } + } + + if (lastProbedLocToFinalSp + STACK_PROBE_BOUNDARY_THRESHOLD_BYTES > STACK_PROBE_PAGE_SIZE_BYTES) + { + Debug.Assert(CheckInstrWord(_target.Read(baseAddress + offset), X86_INSTR_w_TEST_ESP_EAX)); + + // test [esp], eax + offset += 3; + } + + return offset; + } + + private uint SKIP_LEA_EAX_ESP(int val, TargetPointer baseAddress, uint offset) + { + +#if DEBUG + ushort wOpcode = ReadShortAt(baseAddress + offset); + if (CheckInstrWord(wOpcode, X86_INSTR_w_LEA_EAX_ESP_BYTE_OFFSET)) + { + Debug.Assert(val == _target.Read(baseAddress + offset + 3)); + Debug.Assert(CAN_COMPRESS(val)); + } + else + { + Debug.Assert(CheckInstrWord(wOpcode, X86_INSTR_w_LEA_EAX_ESP_DWORD_OFFSET)); + Debug.Assert(val == _target.Read(baseAddress + offset + 3)); + Debug.Assert(!CAN_COMPRESS(val)); + } +#endif + + uint delta = 3u + (CAN_COMPRESS(-val) ? 1u : 4u); + return offset + delta; + } + + private uint SKIP_HELPER_CALL(TargetPointer baseAddress, uint offset) + { + uint delta; + + if (CheckInstrByte(ReadByteAt(baseAddress + offset), X86_INSTR_CALL_REL32)) + { + delta = 5; + } + else + { + Debug.Assert(CheckInstrWord(_target.Read(baseAddress + offset), X86_INSTR_W_CALL_IND_IMM)); + delta = 6; + } + + return offset + delta; + } + + private static bool CAN_COMPRESS(int val) { return ((byte)val) == val; @@ -456,11 +872,30 @@ private static bool CAN_COMPRESS(int val) private const byte X86_INSTR_POP_ECX = 0x59; // pop ecx private const byte X86_INSTR_RET = 0xC2; // ret imm16 private const byte X86_INSTR_JMP_NEAR_REL32 = 0xE9; // near jmp rel32 + private const byte X86_INSTR_PUSH_EAX = 0x50; // push eax + private const byte X86_INSTR_XOR = 0x33; // xor + private const byte X86_INSTR_NOP = 0x90; // nop + private const byte X86_INSTR_RETN = 0xC3; // ret + private const byte X86_INSTR_PUSH_EBP = 0x55; // push ebp + private const ushort X86_INSTR_W_MOV_EBP_ESP = 0xEC8B; // mov ebp, esp + private const byte X86_INSTR_CALL_REL32 = 0xE8; // call rel32 + private const ushort X86_INSTR_W_CALL_IND_IMM = 0x15FF; // call [addr32] private const ushort X86_INSTR_w_JMP_FAR_IND_IMM = 0x25FF; // far jmp [addr32] - + private const ushort X86_INSTR_w_TEST_ESP_EAX = 0x0485; // test [esp], eax private const ushort X86_INSTR_w_LEA_ESP_EBP_BYTE_OFFSET = 0x658d; // lea esp, [ebp-bOffset] private const ushort X86_INSTR_w_LEA_ESP_EBP_DWORD_OFFSET = 0xa58d; // lea esp, [ebp-dwOffset] + private const ushort X86_INSTR_w_TEST_ESP_DWORD_OFFSET_EAX = 0x8485; // test [esp-dwOffset], eax + private const ushort X86_INSTR_w_LEA_EAX_ESP_BYTE_OFFSET = 0x448d; // lea eax, [esp-bOffset] + private const ushort X86_INSTR_w_LEA_EAX_ESP_DWORD_OFFSET = 0x848d; // lea eax, [esp-dwOffset] + + /* Check if the given instruction opcode is the one we expect. + This is a "necessary" but not "sufficient" check as it ignores the check + if the instruction is one of our special markers (for debugging and GcStress) */ + private static bool CheckInstrByte(byte val, byte expectedValue) + { + return (val == expectedValue) || IsMarkerInstr(val); + } /* Similar to CheckInstrByte(). Use this to check a masked opcode (ignoring optional bits in the opcode encoding). From 3fe4a8ad03f6a19b243ed5a19beee952e03b6180 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 27 May 2025 13:11:58 -0400 Subject: [PATCH 07/56] continued testing and bug fixes --- .../StackWalk/Context/ContextHolder.cs | 2 + .../Context/IPlatformAgnosticContext.cs | 1 + .../StackWalk/Context/X86/InfoHdr.cs | 2 +- .../StackWalk/Context/X86/X86Context.cs | 42 ++++++++++++++- .../StackWalk/Context/X86/X86Unwinder.cs | 53 +++++++++++++++---- .../StackWalk/FrameHandling/FrameIterator.cs | 1 + .../FrameHandling/X86FrameHandler.cs | 32 +++++++++++ .../Contracts/StackWalk/StackWalk_1.cs | 35 +++++++++++- .../Legacy/ClrDataStackWalk.cs | 7 +++ .../Legacy/ClrDataTask.cs | 2 +- .../Legacy/SOSDacImpl.cs | 42 +++++++-------- 11 files changed, 183 insertions(+), 36 deletions(-) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs index 3e9091533e6da7..9bb6eca934f1ff 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs @@ -99,4 +99,6 @@ public override bool Equals(object? obj) } public override int GetHashCode() => Context.GetHashCode(); + + public override string ToString() => Context.ToString() ?? string.Empty; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs index cf9aac6ac54507..e687784927153f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs @@ -28,6 +28,7 @@ public static IPlatformAgnosticContext GetContextForPlatform(Target target) IRuntimeInfo runtimeInfo = target.Contracts.RuntimeInfo; return runtimeInfo.GetTargetArchitecture() switch { + RuntimeInfoArchitecture.X86 => new ContextHolder(), RuntimeInfoArchitecture.X64 => new ContextHolder(), RuntimeInfoArchitecture.Arm64 => new ContextHolder(), RuntimeInfoArchitecture.Unknown => throw new InvalidOperationException($"Processor architecture is required for creating a platform specific context and is not provided by the target"), diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs index 234d036cfd02b6..8769c6d373aff5 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs @@ -139,7 +139,7 @@ public enum ReturnKinds public static InfoHdr DecodeHeader(Target target, ref TargetPointer offset, uint codeLength) { - byte nextByte = target.Read(offset); + byte nextByte = target.Read(offset++); byte encoding = (byte)(nextByte & 0x7Fu); if (encoding < 0 || encoding >= INFO_HDR_TABLE.Length) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Context.cs index f097331f2d0f84..3d3e1f107adf2f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Context.cs @@ -24,6 +24,15 @@ public enum ContextFlagsValues : uint CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT, CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS, CONTEXT_XSTATE = CONTEXT_i386 | 0x40, + + // + // This flag is set by the unwinder if it has unwound to a call + // site, and cleared whenever it unwinds through a trap frame. + // It is used by language-specific exception handlers to help + // differentiate exception scopes during dispatching. + // + CONTEXT_UNWOUND_TO_CALL = 0x20000000, + CONTEXT_AREA_MASK = 0xFFFF, } public readonly uint Size => 0x2cc; @@ -229,7 +238,38 @@ public void Unwind(Target target) #endregion [FieldOffset(0xcc)] - public unsafe fixed byte ExtendedRegisters[512]; + public unsafe fixed byte ExtendedRegisters[512]; /// + /// Returns a string representation of the X86Context with all register values except ExtendedRegisters. + /// + /// A formatted string with all register values + public override readonly string ToString() + { + return $"X86Context: " + + $"ContextFlags=0x{ContextFlags:X8} " + + // Debug registers + $"Dr0=0x{Dr0:X8} Dr1=0x{Dr1:X8} Dr2=0x{Dr2:X8} Dr3=0x{Dr3:X8} Dr6=0x{Dr6:X8} Dr7=0x{Dr7:X8} " + + // Floating point control registers + $"ControlWord=0x{ControlWord:X8} StatusWord=0x{StatusWord:X8} TagWord=0x{TagWord:X8} " + + $"ErrorOffset=0x{ErrorOffset:X8} ErrorSelector=0x{ErrorSelector:X4} " + + $"DataOffset=0x{DataOffset:X8} DataSelector=0x{DataSelector:X4} " + + // Floating point registers (displaying only Mantissa and Exponent for each) + // $"ST0.M=0x{ST0.Mantissa:X16} ST0.E=0x{ST0.Exponent:X4} " + + // $"ST1.M=0x{ST1.Mantissa:X16} ST1.E=0x{ST1.Exponent:X4} " + + // $"ST2.M=0x{ST2.Mantissa:X16} ST2.E=0x{ST2.Exponent:X4} " + + // $"ST3.M=0x{ST3.Mantissa:X16} ST3.E=0x{ST3.Exponent:X4} " + + // $"ST4.M=0x{ST4.Mantissa:X16} ST4.E=0x{ST4.Exponent:X4} " + + // $"ST5.M=0x{ST5.Mantissa:X16} ST5.E=0x{ST5.Exponent:X4} " + + // $"ST6.M=0x{ST6.Mantissa:X16} ST6.E=0x{ST6.Exponent:X4} " + + // $"ST7.M=0x{ST7.Mantissa:X16} ST7.E=0x{ST7.Exponent:X4} " + + // $"Cr0NpxState=0x{Cr0NpxState:X8} " + + // Segment registers + $"Gs=0x{Gs:X4} Fs=0x{Fs:X4} Es=0x{Es:X4} Ds=0x{Ds:X4} Cs=0x{Cs:X4} Ss=0x{Ss:X4} " + + // Integer registers + $"Edi=0x{Edi:X8} Esi=0x{Esi:X8} Ebx=0x{Ebx:X8} Edx=0x{Edx:X8} Ecx=0x{Ecx:X8} Eax=0x{Eax:X8} " + + // Control registers + $"Ebp=0x{Ebp:X8} Eip=0x{Eip:X8} EFlags=0x{EFlags:X8} Esp=0x{Esp:X8}"; + } + } /// diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs index 95a5878e89a43f..c8f47f05e8552b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs @@ -4,6 +4,8 @@ using System; using System.Diagnostics; using System.Linq; +using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; +using static Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86Context; namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; @@ -36,8 +38,8 @@ public bool Unwind(ref X86Context context) eman.GetGCInfo(cbh, out TargetPointer gcInfoAddress, out uint _); uint relOffset = (uint)eman.GetRelativeOffset(cbh).Value; TargetCodePointer methodStart = eman.GetStartAddress(cbh); - // TargetCodePointer funcletStart = eman.GetFuncletStartAddress(cbh); - // bool isFunclet = eman.IsFunclet(cbh); + TargetCodePointer funcletStart = eman.GetFuncletStartAddress(cbh); + bool isFunclet = eman.IsFunclet(cbh); GCInfo gcInfo = new(_target, gcInfoAddress, relOffset); @@ -51,7 +53,6 @@ public bool Unwind(ref X86Context context) { /* Handle ESP frames */ UnwindEspFrame(ref context, gcInfo, methodStart); - return true; } else { @@ -67,6 +68,7 @@ public bool Unwind(ref X86Context context) } } + context.ContextFlags |= (uint)ContextFlagsValues.CONTEXT_UNWOUND_TO_CALL; return true; } @@ -493,8 +495,9 @@ private bool UnwindEbpDoubleAlignFrame( if (!gcInfo.SavedRegsMask.HasFlag(regMask)) continue; - // TODO(cdacX86): set register locations - Console.WriteLine(pSavedRegs); + pSavedRegs -= (uint)_target.PointerSize; + TargetPointer regValueFromStack = _target.ReadPointer(pSavedRegs); + SetRegValue(ref context, regMask, regValueFromStack); } } @@ -564,7 +567,7 @@ we need to know our exact location to determine the callee-saved registers */ /* Increment "offset" in steps to see which callee-saved registers have been pushed already */ - foreach (RegMask regMask in registerOrder) + foreach (RegMask regMask in registerOrder.Reverse()) { if (regMask == RegMask.EBP) continue; @@ -572,9 +575,9 @@ registers have been pushed already */ if (InstructionAlreadyExecuted(offset, curOffs)) { - // TODO(cdacx86): UpdateAllRegs set location?? - // SetLocation(pContext, i, PTR_DWORD(--pSavedRegs)); - Console.WriteLine(pSavedRegs); + pSavedRegs -= (uint)_target.PointerSize; + TargetPointer regValueFromStack = _target.ReadPointer(pSavedRegs); + SetRegValue(ref context, regMask, regValueFromStack); } offset = SKIP_PUSH_REG(methodStart, offset); @@ -858,12 +861,42 @@ private uint SKIP_HELPER_CALL(TargetPointer baseAddress, uint offset) return offset + delta; } - private static bool CAN_COMPRESS(int val) { return ((byte)val) == val; } + private static void SetRegValue(ref X86Context context, RegMask regMask, TargetPointer value) + { + uint regValue = (uint)value; + switch (regMask) + { + case RegMask.EAX: + context.Eax = regValue; + break; + case RegMask.EBX: + context.Ebx = regValue; + break; + case RegMask.ECX: + context.Ecx = regValue; + break; + case RegMask.EDX: + context.Edx = regValue; + break; + case RegMask.EBP: + context.Ebp = regValue; + break; + case RegMask.ESI: + context.Esi = regValue; + break; + case RegMask.EDI: + context.Edi = regValue; + break; + default: + throw new ArgumentException($"Unsupported register mask: {regMask}"); + } + } + #endregion #region Verification Helpers diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs index 56fdae3c78c623..5e7774e39d507e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs @@ -177,6 +177,7 @@ private IPlatformFrameHandler GetFrameHandler(IPlatformAgnosticContext context) { return context switch { + ContextHolder contextHolder => new X86FrameHandler(target, contextHolder), ContextHolder contextHolder => new AMD64FrameHandler(target, contextHolder), ContextHolder contextHolder => new ARM64FrameHandler(target, contextHolder), _ => throw new InvalidOperationException("Unsupported context type"), diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs new file mode 100644 index 00000000000000..8500f64e295b80 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Microsoft.Diagnostics.DataContractReader.Data; +using static Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86Context; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +internal class X86FrameHandler(Target target, ContextHolder contextHolder) : BaseFrameHandler(target, contextHolder), IPlatformFrameHandler +{ + private readonly ContextHolder _holder = contextHolder; + + void IPlatformFrameHandler.HandleFaultingExceptionFrame(FaultingExceptionFrame frame) + { + if (frame.TargetContext is not TargetPointer targetContext) + { + throw new InvalidOperationException("Unexpected null context pointer on FaultingExceptionFrame"); + } + _holder.ReadFromAddress(_target, targetContext); + + // Clear the CONTEXT_XSTATE, since the X86Context contains just plain CONTEXT structure + // that does not support holding any extended state. + _holder.Context.ContextFlags &= ~(uint)(ContextFlagsValues.CONTEXT_XSTATE & ContextFlagsValues.CONTEXT_AREA_MASK); + } + + void IPlatformFrameHandler.HandleHijackFrame(HijackFrame frame) + { + // TODO(cdacX86): + throw new NotImplementedException(); + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs index 633283e91de529..031893cb287192 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs @@ -12,10 +12,12 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; internal readonly struct StackWalk_1 : IStackWalk { private readonly Target _target; + private readonly RuntimeInfoArchitecture _arch; internal StackWalk_1(Target target) { _target = target; + _arch = target.Contracts.RuntimeInfo.GetTargetArchitecture(); } public enum StackWalkState @@ -91,7 +93,7 @@ private bool Next(StackWalkData handle) break; case StackWalkState.SW_FRAME: handle.FrameIter.UpdateContextFromFrame(handle.Context); - if (!handle.FrameIter.IsInlineCallFrameWithActiveCall()) + if (_arch == RuntimeInfoArchitecture.X86 || !handle.FrameIter.IsInlineCallFrameWithActiveCall()) { handle.FrameIter.Next(); } @@ -131,6 +133,18 @@ private void UpdateState(StackWalkData handle) } } + // + // If an explicit frame is allocated in a managed stack frame (e.g. an inlined pinvoke call), + // we may have skipped an explicit frame. This function checks for them. + // + // Return Value: + // Returns true if there are skipped frames. + // + // Notes: + // x86 wants to stop at the skipped stack frames after the containing managed stack frame, but + // WIN64 wants to stop before. I don't think x86 actually has any good reason for this, except + // because it doesn't unwind one frame ahead of time like WIN64 does. This means that we don't + // have the caller SP on x86. private bool CheckForSkippedFrames(StackWalkData handle) { // ensure we can find the caller context @@ -146,7 +160,24 @@ private bool CheckForSkippedFrames(StackWalkData handle) IPlatformAgnosticContext parentContext = handle.Context.Clone(); parentContext.Unwind(_target); - return handle.FrameIter.CurrentFrameAddress.Value < parentContext.StackPointer.Value; + if (handle.FrameIter.CurrentFrameAddress.Value >= parentContext.StackPointer.Value) + return false; + + while (handle.FrameIter.IsValid() && + handle.FrameIter.CurrentFrameAddress.Value < parentContext.StackPointer.Value) + { + if (_arch == RuntimeInfoArchitecture.X86 && handle.FrameIter.IsInlineCallFrameWithActiveCall()) + { + // On x86 we have already reported the InlinedCallFrame, don't report it again. + handle.FrameIter.Next(); + } + else + { + return true; + } + } + + return false; } byte[] IStackWalk.GetRawContext(IStackDataFrameHandle stackDataFrameHandle) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs index 8440611106be13..86ccd88793dea3 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using Microsoft.Diagnostics.DataContractReader.Contracts; @@ -76,6 +77,12 @@ int IXCLRDataStackWalk.GetContext(uint contextFlags, uint contextBufSize, uint* contextStruct.FillFromBuffer(contextBuf); localContextStruct.FillFromBuffer(localContextBuf); + using (StreamWriter outputFile = new StreamWriter("C:\\Users\\maxcharlamb\\OneDrive - Microsoft\\Desktop\\out.txt", true)) + { + outputFile.WriteLine($"cDAC: {contextStruct}"); + outputFile.WriteLine($" DAC: {localContextStruct}"); + } + Debug.Assert(contextStruct.Equals(localContextStruct)); } } diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataTask.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataTask.cs index 3c189701e57fd5..0b4539829d4563 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataTask.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataTask.cs @@ -54,7 +54,7 @@ int IXCLRDataTask.CreateStackWalk(uint flags, out IXCLRDataStackWalk? stackWalk) } stackWalk = new ClrDataStackWalk(_address, flags, _target, legacyStackWalk); - return HResults.S_OK; + return HResults.E_BOUNDS; } int IXCLRDataTask.GetOSThreadID(uint* id) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 64787725781241..6d3c470d3d649b 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -806,27 +806,27 @@ int ISOSDacInterface.GetMethodDescPtrFromIP(ulong ip, ulong* ppMD) { TargetPointer methodDescAddr = executionManager.GetMethodDesc(codeHandle); - TargetCodePointer funcletStart = executionManager.GetFuncletStartAddress(codeHandle); - bool isFunclet = executionManager.IsFunclet(codeHandle); - TargetPointer unwindInfo = executionManager.GetUnwindInfo(codeHandle); - executionManager.GetGCInfo(codeHandle, out TargetPointer gcInfoPtr, out uint gcVersion); - TargetNUInt relOffset = executionManager.GetRelativeOffset(codeHandle); - GCInfo gcInfo = new(_target, gcInfoPtr, (uint)relOffset.Value); - - using (StreamWriter outputFile = new StreamWriter("C:\\Users\\maxcharlamb\\OneDrive - Microsoft\\Desktop\\out.txt", true)) - { - foreach (int offset in gcInfo.Transitions.Keys) - { - // outputFile.WriteLine($"CodeOffset: {offset:x8}"); - foreach (BaseGcTransition gcTransition in gcInfo.Transitions[offset]) - { - // outputFile.WriteLine(gcTransition); - } - } - } - Console.WriteLine(gcInfo.Transitions); - Console.WriteLine($"IP: {ip:x}, MethodDesc: {methodDescAddr.Value:x}, FuncletStart: {funcletStart.Value:x}, IsFunclet: {isFunclet}, UnwindInfo: {unwindInfo.Value:x}"); - Console.WriteLine($"GCInfo: {gcInfoPtr.Value:x}, GCVersion: {gcVersion}"); + // TargetCodePointer funcletStart = executionManager.GetFuncletStartAddress(codeHandle); + // bool isFunclet = executionManager.IsFunclet(codeHandle); + // TargetPointer unwindInfo = executionManager.GetUnwindInfo(codeHandle); + // executionManager.GetGCInfo(codeHandle, out TargetPointer gcInfoPtr, out uint gcVersion); + // TargetNUInt relOffset = executionManager.GetRelativeOffset(codeHandle); + // GCInfo gcInfo = new(_target, gcInfoPtr, (uint)relOffset.Value); + + // using (StreamWriter outputFile = new StreamWriter("C:\\Users\\maxcharlamb\\OneDrive - Microsoft\\Desktop\\out.txt", true)) + // { + // foreach (int offset in gcInfo.Transitions.Keys) + // { + // // outputFile.WriteLine($"CodeOffset: {offset:x8}"); + // foreach (BaseGcTransition gcTransition in gcInfo.Transitions[offset]) + // { + // // outputFile.WriteLine(gcTransition); + // } + // } + // } + // Console.WriteLine(gcInfo.Transitions); + // Console.WriteLine($"IP: {ip:x}, MethodDesc: {methodDescAddr.Value:x}, FuncletStart: {funcletStart.Value:x}, IsFunclet: {isFunclet}, UnwindInfo: {unwindInfo.Value:x}"); + // Console.WriteLine($"GCInfo: {gcInfoPtr.Value:x}, GCVersion: {gcVersion}"); try { From 6b5801278431a7b66572d61b3adf97a40afdfa09 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 28 May 2025 09:57:03 -0400 Subject: [PATCH 08/56] fix issues with ICFs --- .../StackWalk/Context/X86/GCInfoDecoder.cs | 2 ++ .../StackWalk/Context/X86/InfoHdr.cs | 1 + .../FrameHandling/X86FrameHandler.cs | 6 ++--- .../Contracts/StackWalk/StackWalk_1.cs | 27 ++++++++++++++++--- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs index 2d8eb8c7c59938..aa07167f8e5089 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs @@ -43,6 +43,8 @@ public record GCInfo public bool IsInEpilog => EpilogOffset != unchecked((uint)-1); public uint EpilogOffset { get; set; } = unchecked((uint)-1); + public bool HasReversePInvoke => Header.RevPInvokeOffset != InfoHdr.INVALID_REV_PINVOKE_OFFSET; + public uint RawStackSize { get; set; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs index 8769c6d373aff5..6459dc7a61af41 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs @@ -387,6 +387,7 @@ public static InfoHdr DecodeHeader(Target target, ref TargetPointer offset, uint public const uint HAS_UNTRACKED = 0xFFFFFFFF; public const uint HAS_GS_COOKIE_OFFSET = 0xFFFFFFFF; public const uint HAS_SYNC_OFFSET = 0xFFFFFFFF; + public const uint INVALID_REV_PINVOKE_OFFSET = unchecked((uint)-1); public const uint HAS_REV_PINVOKE_FRAME_OFFSET = unchecked((uint)-2); public const uint HAS_NOGCREGIONS = 0xFFFFFFFF; private const uint YES = HAS_VARPTR; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs index 8500f64e295b80..2ae2b69ea3492c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs @@ -9,7 +9,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; internal class X86FrameHandler(Target target, ContextHolder contextHolder) : BaseFrameHandler(target, contextHolder), IPlatformFrameHandler { - private readonly ContextHolder _holder = contextHolder; + private readonly ContextHolder _context = contextHolder; void IPlatformFrameHandler.HandleFaultingExceptionFrame(FaultingExceptionFrame frame) { @@ -17,11 +17,11 @@ void IPlatformFrameHandler.HandleFaultingExceptionFrame(FaultingExceptionFrame f { throw new InvalidOperationException("Unexpected null context pointer on FaultingExceptionFrame"); } - _holder.ReadFromAddress(_target, targetContext); + _context.ReadFromAddress(_target, targetContext); // Clear the CONTEXT_XSTATE, since the X86Context contains just plain CONTEXT structure // that does not support holding any extended state. - _holder.Context.ContextFlags &= ~(uint)(ContextFlagsValues.CONTEXT_XSTATE & ContextFlagsValues.CONTEXT_AREA_MASK); + _context.Context.ContextFlags &= ~(uint)(ContextFlagsValues.CONTEXT_XSTATE & ContextFlagsValues.CONTEXT_AREA_MASK); } void IPlatformFrameHandler.HandleHijackFrame(HijackFrame frame) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs index 031893cb287192..994c13924d818e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Diagnostics; using System.Collections.Generic; +using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -75,6 +76,7 @@ IEnumerable IStackWalk.CreateStackWalk(ThreadData threadD private bool Next(StackWalkData handle) { + IPlatformAgnosticContext prevContext = handle.Context.Clone(); switch (handle.State) { case StackWalkState.SW_FRAMELESS: @@ -102,12 +104,12 @@ private bool Next(StackWalkData handle) case StackWalkState.SW_COMPLETE: return false; } - UpdateState(handle); + UpdateState(handle, prevContext); return handle.State is not (StackWalkState.SW_ERROR or StackWalkState.SW_COMPLETE); } - private void UpdateState(StackWalkData handle) + private void UpdateState(StackWalkData handle, IPlatformAgnosticContext prevContext) { // If we are complete or in a bad state, no updating is required. if (handle.State is StackWalkState.SW_ERROR or StackWalkState.SW_COMPLETE) @@ -118,6 +120,23 @@ private void UpdateState(StackWalkData handle) bool isManaged = IsManaged(handle.Context.InstructionPointer, out _); bool validFrame = handle.FrameIter.IsValid(); + if (_arch == RuntimeInfoArchitecture.X86 && IsManaged(prevContext.InstructionPointer, out CodeBlockHandle? cbh)) + { + IExecutionManager eman = _target.Contracts.ExecutionManager; + + if (!eman.IsFunclet(cbh.Value)) + { + eman.GetGCInfo(cbh.Value, out TargetPointer gcInfoAddress, out uint _); + uint relOffset = (uint)eman.GetRelativeOffset(cbh.Value).Value; + GCInfo gcInfo = new(_target, gcInfoAddress, relOffset); + if (gcInfo.HasReversePInvoke) + { + handle.State = StackWalkState.SW_FRAME; + return; + } + } + } + if (isManaged) { handle.State = StackWalkState.SW_FRAMELESS; @@ -160,8 +179,8 @@ private bool CheckForSkippedFrames(StackWalkData handle) IPlatformAgnosticContext parentContext = handle.Context.Clone(); parentContext.Unwind(_target); - if (handle.FrameIter.CurrentFrameAddress.Value >= parentContext.StackPointer.Value) - return false; + long stackDifference = (long)parentContext.StackPointer.Value - (long)handle.FrameIter.CurrentFrameAddress.Value; + Console.WriteLine(stackDifference); while (handle.FrameIter.IsValid() && handle.FrameIter.CurrentFrameAddress.Value < parentContext.StackPointer.Value) From fba0091a844fc5b0f4bb99ec88c33182b43e0e81 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 28 May 2025 11:43:31 -0400 Subject: [PATCH 09/56] add docs for ExecutionManager changes --- docs/design/datacontracts/ExecutionManager.md | 30 ++++++-- .../ExecutionManagerCore.EEJitManager.cs | 48 ++++++------- ...ecutionManagerCore.ReadyToRunJitManager.cs | 72 +++++++++---------- 3 files changed, 86 insertions(+), 64 deletions(-) diff --git a/docs/design/datacontracts/ExecutionManager.md b/docs/design/datacontracts/ExecutionManager.md index 9075ee2548b643..0bc00f583a2f30 100644 --- a/docs/design/datacontracts/ExecutionManager.md +++ b/docs/design/datacontracts/ExecutionManager.md @@ -23,10 +23,16 @@ struct CodeBlockHandle TargetPointer GetMethodDesc(CodeBlockHandle codeInfoHandle); // Get the instruction pointer address of the start of the code block TargetCodePointer GetStartAddress(CodeBlockHandle codeInfoHandle); + // Get the instruction pointer address of the start of the funclet containing the code block + TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle); // Gets the unwind info of the code block at the specified code pointer - TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle, TargetCodePointer ip); - // Gets the base address the UnwindInfo of codeInfoHandle is relative to. + TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle); + // Gets the base address the UnwindInfo of codeInfoHandle is relative to TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle); + // Gets the GCInfo associated with the code block and its version + void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion); + // Gets the offset of the codeInfoHandle inside of the code block + TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle); ``` ## Version 1 @@ -59,6 +65,7 @@ Data descriptors used: | `RealCodeHeader` | `MethodDesc` | Pointer to the corresponding `MethodDesc` | | `RealCodeHeader` | `NumUnwindInfos` | Number of Unwind Infos | | `RealCodeHeader` | `UnwindInfos` | Start address of Unwind Infos | +| `RealCodeHeader` | `GCInfo` | Pointer to the GCInfo encoding | | `Module` | `ReadyToRunInfo` | Pointer to the `ReadyToRunInfo` for the module | | `ReadyToRunInfo` | `CompositeInfo` | Pointer to composite R2R info - or itself for non-composite | | `ReadyToRunInfo` | `NumRuntimeFunctions` | Number of `RuntimeFunctions` | @@ -220,7 +227,7 @@ class CodeBlock } ``` -The `GetMethodDesc` and `GetStartAddress` APIs extract fields of the `CodeBlock`: +The `GetMethodDesc`, `GetStartAddress`, and `GetRelativeOffset` APIs extract fields of the `CodeBlock`: ```csharp TargetPointer IExecutionManager.GetMethodDesc(CodeBlockHandle codeInfoHandle) @@ -234,9 +241,15 @@ The `GetMethodDesc` and `GetStartAddress` APIs extract fields of the `CodeBlock` /* find CodeBlock info for codeInfoHandle.Address*/ return info.StartAddress; } + + TargetNUInt IExecutionManager.GetRelativeOffset(CodeBlockHandle codeInfoHandle) + { + /* find CodeBlock info for codeInfoHandle.Address*/ + return info.RelativeOffset; + } ``` -`GetUnwindInfo` gets the Windows style unwind data in the form of `RUNTIME_FUNCTION` which has a platform dependent implementation. The ExecutionManager delegates to the JitManager implementations as the unwind infos (`RUNTIME_FUNCTION`) are stored differently on jitted and R2R code. +`IExecutionManager.GetUnwindInfo` gets the Windows style unwind data in the form of `RUNTIME_FUNCTION` which has a platform dependent implementation. The ExecutionManager delegates to the JitManager implementations as the unwind infos (`RUNTIME_FUNCTION`) are stored differently on jitted and R2R code. * For jitted code (`EEJitManager`) a list of sorted `RUNTIME_FUNCTION` are stored on the `RealCodeHeader` which is accessed in the same was as `GetMethodInfo` described above. The correct `RUNTIME_FUNCTION` is found by binary searching the list based on IP. @@ -245,6 +258,15 @@ The `GetMethodDesc` and `GetStartAddress` APIs extract fields of the `CodeBlock` Unwind info (`RUNTIME_FUNCTION`) use relative addressing. For managed code, these values are relative to the start of the code's containing range in the RangeSectionMap (described below). This could be the beginning of a `CodeHeap` for jitted code or the base address of the loaded image for ReadyToRun code. `GetUnwindInfoBaseAddress` finds this base address for a given `CodeBlockHandle`. +`IExecutionManager.GetGCInfo` gets a pointer to the relevant GCInfo for a `CodeBlockHandle`. The ExecutionManager delegates to the JitManager implementations as the GCInfo is stored differently on jitted and R2R code. + +* For jitted code (`EEJitManager`) a pointer to the `GCInfo` is stored on the `RealCodeHeader` which is accessed in the same was as `GetMethodInfo` described above. This can simply be returned as is. + +* For R2R code (`ReadyToRunJitManager`), the `GCInfo` is stored directly after the `UnwindData`. This in turn is found by looking up the `UnwindInfo` (`RUNTIME_FUNCTION`) and reading the `UnwindData` offset. We find the `UnwindInfo` as described above in `IExecutionManager.GetUnwindInfo`. Once we have the relevant unwind data, we calculate the size of the unwind data and return a pointer to the following byte (first byte of the GCInfo). +// TODO(cdacX86): Add information on finding the size of the unwind info. + +`IExecutionManager.GetFuncletStartAddress` finds the start of the code blocks funclet. This will be different than the methods start address `GetStartAddress` if the current code block is inside of a funclet. To find the funclet start address, we get the unwind info corresponding to the code block using `IExecutionManager.GetUnwindInfo`. We then parse the unwind info to find the begin address (relative to the unwind info base address) and return the unwind info base address + unwind info begin address. + ### RangeSectionMap The range section map logically partitions the entire 32-bit or 64-bit addressable space into chunks. diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs index 0a05adab6f8855..b408fcaaa30aa8 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs @@ -22,30 +22,6 @@ public EEJitManager(Target target, INibbleMap nibbleMap) : base(target) _runtimeFunctions = RuntimeFunctionLookup.Create(target); } - public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion) - { - gcInfo = TargetPointer.Null; - gcVersion = 0; - - // EEJitManager::GetGCInfoToken - if (rangeSection.IsRangeList) - return; - - if (rangeSection.Data == null) - throw new ArgumentException(nameof(rangeSection)); - - TargetPointer codeStart = FindMethodCode(rangeSection, jittedCodeAddress); - if (codeStart == TargetPointer.Null) - return; - Debug.Assert(codeStart.Value <= jittedCodeAddress.Value); - - if (!GetRealCodeHeader(rangeSection, codeStart, out Data.RealCodeHeader? realCodeHeader)) - return; - - gcVersion = GCINFO_VERSION; - gcInfo = realCodeHeader.GCInfo; - } - public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) { info = null; @@ -111,6 +87,30 @@ public override TargetPointer GetUnwindInfo(RangeSection rangeSection, TargetCod return _runtimeFunctions.GetRuntimeFunctionAddress(unwindInfos, index); } + public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion) + { + gcInfo = TargetPointer.Null; + gcVersion = 0; + + // EEJitManager::GetGCInfoToken + if (rangeSection.IsRangeList) + return; + + if (rangeSection.Data == null) + throw new ArgumentException(nameof(rangeSection)); + + TargetPointer codeStart = FindMethodCode(rangeSection, jittedCodeAddress); + if (codeStart == TargetPointer.Null) + return; + Debug.Assert(codeStart.Value <= jittedCodeAddress.Value); + + if (!GetRealCodeHeader(rangeSection, codeStart, out Data.RealCodeHeader? realCodeHeader)) + return; + + gcVersion = GCINFO_VERSION; + gcInfo = realCodeHeader.GCInfo; + } + private TargetPointer FindMethodCode(RangeSection rangeSection, TargetCodePointer jittedCodeAddress) { // EEJitManager::FindMethodCode diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index bbf51b9a3ba787..0c42a728266c9c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -27,42 +27,6 @@ public ReadyToRunJitManager(Target target) : base(target) _runtimeFunctions = RuntimeFunctionLookup.Create(target); } - public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion) - { - gcInfo = TargetPointer.Null; - gcVersion = 0; - - // ReadyToRunJitManager::GetGCInfoToken - if (rangeSection.Data == null) - throw new ArgumentException(nameof(rangeSection)); - - Debug.Assert(rangeSection.Data.R2RModule != TargetPointer.Null); - - Data.Module r2rModule = Target.ProcessedData.GetOrAdd(rangeSection.Data.R2RModule); - Debug.Assert(r2rModule.ReadyToRunInfo != TargetPointer.Null); - Data.ReadyToRunInfo r2rInfo = Target.ProcessedData.GetOrAdd(r2rModule.ReadyToRunInfo); - - // Check if address is in a thunk - if (IsStubCodeBlockThunk(rangeSection.Data, r2rInfo, jittedCodeAddress)) - return; - - // Find the relative address that we are looking for - TargetPointer addr = CodePointerUtils.AddressFromCodePointer(jittedCodeAddress, Target); - TargetPointer imageBase = rangeSection.Data.RangeBegin; - TargetPointer relativeAddr = addr - imageBase; - - uint index; - if (!_runtimeFunctions.TryGetRuntimeFunctionIndexForAddress(r2rInfo.RuntimeFunctions, r2rInfo.NumRuntimeFunctions, relativeAddr, out index)) - return; - - Data.RuntimeFunction runtimeFunction = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, index); - - TargetPointer unwindInfo = runtimeFunction.UnwindData + imageBase; - uint unwindDataSize = sizeof(uint); // TODO(cdac): This is platform specific and maybe needs its own contract. Current value is for x86 - gcInfo = unwindInfo + unwindDataSize; - gcVersion = GCINFO_VERSION; // TODO(cdac): This depends on the major version of the runtime. - } - public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) { // ReadyToRunJitManager::JitCodeToMethodInfo @@ -160,6 +124,42 @@ public override TargetPointer GetUnwindInfo(RangeSection rangeSection, TargetCod return _runtimeFunctions.GetRuntimeFunctionAddress(r2rInfo.RuntimeFunctions, index); } + public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion) + { + gcInfo = TargetPointer.Null; + gcVersion = 0; + + // ReadyToRunJitManager::GetGCInfoToken + if (rangeSection.Data == null) + throw new ArgumentException(nameof(rangeSection)); + + Debug.Assert(rangeSection.Data.R2RModule != TargetPointer.Null); + + Data.Module r2rModule = Target.ProcessedData.GetOrAdd(rangeSection.Data.R2RModule); + Debug.Assert(r2rModule.ReadyToRunInfo != TargetPointer.Null); + Data.ReadyToRunInfo r2rInfo = Target.ProcessedData.GetOrAdd(r2rModule.ReadyToRunInfo); + + // Check if address is in a thunk + if (IsStubCodeBlockThunk(rangeSection.Data, r2rInfo, jittedCodeAddress)) + return; + + // Find the relative address that we are looking for + TargetPointer addr = CodePointerUtils.AddressFromCodePointer(jittedCodeAddress, Target); + TargetPointer imageBase = rangeSection.Data.RangeBegin; + TargetPointer relativeAddr = addr - imageBase; + + uint index; + if (!_runtimeFunctions.TryGetRuntimeFunctionIndexForAddress(r2rInfo.RuntimeFunctions, r2rInfo.NumRuntimeFunctions, relativeAddr, out index)) + return; + + Data.RuntimeFunction runtimeFunction = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, index); + + TargetPointer unwindInfo = runtimeFunction.UnwindData + imageBase; + uint unwindDataSize = sizeof(uint); // TODO(cdac): This is platform specific and maybe needs its own contract. Current value is for x86 + gcInfo = unwindInfo + unwindDataSize; + gcVersion = GCINFO_VERSION; // TODO(cdac): This depends on the major version of the runtime. + } + private bool IsStubCodeBlockThunk(Data.RangeSection rangeSection, Data.ReadyToRunInfo r2rInfo, TargetCodePointer jittedCodeAddress) { if (r2rInfo.DelayLoadMethodCallThunks == TargetPointer.Null) From 7f6b3887097005ed05d6b042eeed6049ddf07750 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 28 May 2025 11:46:06 -0400 Subject: [PATCH 10/56] add doc for IsFunclet --- docs/design/datacontracts/ExecutionManager.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/design/datacontracts/ExecutionManager.md b/docs/design/datacontracts/ExecutionManager.md index 0bc00f583a2f30..555e2c6d88b549 100644 --- a/docs/design/datacontracts/ExecutionManager.md +++ b/docs/design/datacontracts/ExecutionManager.md @@ -33,6 +33,9 @@ struct CodeBlockHandle void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion); // Gets the offset of the codeInfoHandle inside of the code block TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle); + + // Extension Methods (implemented in terms of other APIs) + bool IsFunclet(CodeBlockHandle codeInfoHandle); ``` ## Version 1 @@ -267,6 +270,8 @@ Unwind info (`RUNTIME_FUNCTION`) use relative addressing. For managed code, thes `IExecutionManager.GetFuncletStartAddress` finds the start of the code blocks funclet. This will be different than the methods start address `GetStartAddress` if the current code block is inside of a funclet. To find the funclet start address, we get the unwind info corresponding to the code block using `IExecutionManager.GetUnwindInfo`. We then parse the unwind info to find the begin address (relative to the unwind info base address) and return the unwind info base address + unwind info begin address. +`IsFunclet` is implemented in terms of `IExecutionManager.GetStartAddress` and `IExecutionManager.GetFuncletStartAddress`. If the values are the same, the code block handle is not a funclet. If they are different, it is a funclet. + ### RangeSectionMap The range section map logically partitions the entire 32-bit or 64-bit addressable space into chunks. From 2917ef99bbbd0812a0d0ccdf329f7636a8592c0f Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 28 May 2025 13:24:47 -0400 Subject: [PATCH 11/56] fix some build issues --- .../Contracts/StackWalk/Context/X86/X86Unwinder.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs index c8f47f05e8552b..eb05e4bc136485 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs @@ -824,8 +824,6 @@ private uint SKIP_ALLOC_FRAME(int size, TargetPointer baseAddress, uint offset) private uint SKIP_LEA_EAX_ESP(int val, TargetPointer baseAddress, uint offset) { - -#if DEBUG ushort wOpcode = ReadShortAt(baseAddress + offset); if (CheckInstrWord(wOpcode, X86_INSTR_w_LEA_EAX_ESP_BYTE_OFFSET)) { @@ -838,7 +836,6 @@ private uint SKIP_LEA_EAX_ESP(int val, TargetPointer baseAddress, uint offset) Debug.Assert(val == _target.Read(baseAddress + offset + 3)); Debug.Assert(!CAN_COMPRESS(val)); } -#endif uint delta = 3u + (CAN_COMPRESS(-val) ? 1u : 4u); return offset + delta; From 34a7169a2bb06200d4bbaa5b21f56aff1b5450f1 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 28 May 2025 13:31:01 -0400 Subject: [PATCH 12/56] move gcinfo decoding into folder --- .../Context/X86/{ => GCInfoDecoding}/CallPattern.cs | 0 .../X86/{GCInfoDecoder.cs => GCInfoDecoding/GCInfo.cs} | 0 .../X86/{ => GCInfoDecoding}/GCInfoTargetExtensions.cs | 0 .../Context/X86/{ => GCInfoDecoding}/GCSlotTable.cs | 0 .../Context/X86/{ => GCInfoDecoding}/GCTransition.cs | 0 .../Context/X86/{ => GCInfoDecoding}/InfoHdr.cs | 10 +++++----- .../X86/{ => GCInfoDecoding}/NoGcRegionTable.cs | 0 7 files changed, 5 insertions(+), 5 deletions(-) rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/{ => GCInfoDecoding}/CallPattern.cs (100%) rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/{GCInfoDecoder.cs => GCInfoDecoding/GCInfo.cs} (100%) rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/{ => GCInfoDecoding}/GCInfoTargetExtensions.cs (100%) rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/{ => GCInfoDecoding}/GCSlotTable.cs (100%) rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/{ => GCInfoDecoding}/GCTransition.cs (100%) rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/{ => GCInfoDecoding}/InfoHdr.cs (99%) rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/{ => GCInfoDecoding}/NoGcRegionTable.cs (100%) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/CallPattern.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/CallPattern.cs similarity index 100% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/CallPattern.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/CallPattern.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs similarity index 100% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoder.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoTargetExtensions.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfoTargetExtensions.cs similarity index 100% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoTargetExtensions.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfoTargetExtensions.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCSlotTable.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCSlotTable.cs similarity index 100% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCSlotTable.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCSlotTable.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCTransition.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCTransition.cs similarity index 100% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCTransition.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCTransition.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs similarity index 99% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs index 6459dc7a61af41..5c245b3d4a963a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/InfoHdr.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs @@ -74,11 +74,11 @@ public record struct InfoHdr private enum InfoHdrAdjust : byte { - SET_FRAMESIZE = 0, // 0x00 - SET_ARGCOUNT = SET_FRAMESIZE + SET_FRAMESIZE_MAX + 1, // 0x08 - SET_PROLOGSIZE = SET_ARGCOUNT + SET_ARGCOUNT_MAX + 1, // 0x11 - SET_EPILOGSIZE = SET_PROLOGSIZE + SET_PROLOGSIZE_MAX + 1, // 0x22 - SET_EPILOGCNT = SET_EPILOGSIZE + SET_EPILOGSIZE_MAX + 1, // 0x2d + SET_FRAMESIZE = 0, // 0x00 + SET_ARGCOUNT = SET_FRAMESIZE + SET_FRAMESIZE_MAX + 1, // 0x08 + SET_PROLOGSIZE = SET_ARGCOUNT + SET_ARGCOUNT_MAX + 1, // 0x11 + SET_EPILOGSIZE = SET_PROLOGSIZE + SET_PROLOGSIZE_MAX + 1, // 0x22 + SET_EPILOGCNT = SET_EPILOGSIZE + SET_EPILOGSIZE_MAX + 1, // 0x2d SET_UNTRACKED = SET_EPILOGCNT + (SET_EPILOGCNT_MAX + 1) * 2, // 0x37 FIRST_FLIP = SET_UNTRACKED + SET_UNTRACKED_MAX + 1, diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/NoGcRegionTable.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/NoGcRegionTable.cs similarity index 100% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/NoGcRegionTable.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/NoGcRegionTable.cs From 1b19fb27619f19944e0e4bb198bd560e4f617fff Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 28 May 2025 13:33:20 -0400 Subject: [PATCH 13/56] move x86 context next to other contexts --- .../Contracts/StackWalk/Context/{X86 => }/X86Context.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/{X86 => }/X86Context.cs (100%) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs similarity index 100% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Context.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs From 3092546ac95cf4aa9f669e77def1b52744752e15 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 28 May 2025 13:57:00 -0400 Subject: [PATCH 14/56] add some doc comments --- .../Contracts/StackWalk/StackWalk_1.cs | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs index 994c13924d818e..a23c2fd94988b4 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs @@ -120,19 +120,33 @@ private void UpdateState(StackWalkData handle, IPlatformAgnosticContext prevCont bool isManaged = IsManaged(handle.Context.InstructionPointer, out _); bool validFrame = handle.FrameIter.IsValid(); - if (_arch == RuntimeInfoArchitecture.X86 && IsManaged(prevContext.InstructionPointer, out CodeBlockHandle? cbh)) - { - IExecutionManager eman = _target.Contracts.ExecutionManager; + // + // Platform specific checks + // - if (!eman.IsFunclet(cbh.Value)) + // x86 Specific Checks + if (_arch == RuntimeInfoArchitecture.X86) + { + if (IsManaged(prevContext.InstructionPointer, out CodeBlockHandle? cbh)) { - eman.GetGCInfo(cbh.Value, out TargetPointer gcInfoAddress, out uint _); - uint relOffset = (uint)eman.GetRelativeOffset(cbh.Value).Value; - GCInfo gcInfo = new(_target, gcInfoAddress, relOffset); - if (gcInfo.HasReversePInvoke) + IExecutionManager eman = _target.Contracts.ExecutionManager; + + if (!eman.IsFunclet(cbh.Value)) { - handle.State = StackWalkState.SW_FRAME; - return; + eman.GetGCInfo(cbh.Value, out TargetPointer gcInfoAddress, out uint _); + uint relOffset = (uint)eman.GetRelativeOffset(cbh.Value).Value; + GCInfo gcInfo = new(_target, gcInfoAddress, relOffset); + if (gcInfo.HasReversePInvoke) + { + // The managed frame we've unwound from had reverse PInvoke frame. Since we are on a frameless + // frame, that means that the method was called from managed code without any native frames in between. + // On x86, the InlinedCallFrame of the pinvoke would get skipped as we've just unwound to the pinvoke IL stub and + // for this architecture, the inlined call frames are supposed to be processed before the managed frame they are stored in. + // So we force the stack frame iterator to process the InlinedCallFrame before the IL stub. + Debug.Assert(handle.FrameIter.IsInlineCallFrameWithActiveCall()); + handle.State = StackWalkState.SW_FRAME; + return; + } } } } @@ -179,9 +193,6 @@ private bool CheckForSkippedFrames(StackWalkData handle) IPlatformAgnosticContext parentContext = handle.Context.Clone(); parentContext.Unwind(_target); - long stackDifference = (long)parentContext.StackPointer.Value - (long)handle.FrameIter.CurrentFrameAddress.Value; - Console.WriteLine(stackDifference); - while (handle.FrameIter.IsValid() && handle.FrameIter.CurrentFrameAddress.Value < parentContext.StackPointer.Value) { From 3b0241937a0156dbe427c0e76ed5f21da60cac3a Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 28 May 2025 14:11:08 -0400 Subject: [PATCH 15/56] remove debug print helpers --- .../Contracts/StackWalk/Context/X86Context.cs | 33 +------------------ .../Legacy/ClrDataStackWalk.cs | 6 ---- .../Legacy/ClrDataTask.cs | 2 +- .../Legacy/SOSDacImpl.cs | 24 -------------- 4 files changed, 2 insertions(+), 63 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs index 3d3e1f107adf2f..03f7f5600c3c62 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs @@ -238,38 +238,7 @@ public void Unwind(Target target) #endregion [FieldOffset(0xcc)] - public unsafe fixed byte ExtendedRegisters[512]; /// - /// Returns a string representation of the X86Context with all register values except ExtendedRegisters. - /// - /// A formatted string with all register values - public override readonly string ToString() - { - return $"X86Context: " + - $"ContextFlags=0x{ContextFlags:X8} " + - // Debug registers - $"Dr0=0x{Dr0:X8} Dr1=0x{Dr1:X8} Dr2=0x{Dr2:X8} Dr3=0x{Dr3:X8} Dr6=0x{Dr6:X8} Dr7=0x{Dr7:X8} " + - // Floating point control registers - $"ControlWord=0x{ControlWord:X8} StatusWord=0x{StatusWord:X8} TagWord=0x{TagWord:X8} " + - $"ErrorOffset=0x{ErrorOffset:X8} ErrorSelector=0x{ErrorSelector:X4} " + - $"DataOffset=0x{DataOffset:X8} DataSelector=0x{DataSelector:X4} " + - // Floating point registers (displaying only Mantissa and Exponent for each) - // $"ST0.M=0x{ST0.Mantissa:X16} ST0.E=0x{ST0.Exponent:X4} " + - // $"ST1.M=0x{ST1.Mantissa:X16} ST1.E=0x{ST1.Exponent:X4} " + - // $"ST2.M=0x{ST2.Mantissa:X16} ST2.E=0x{ST2.Exponent:X4} " + - // $"ST3.M=0x{ST3.Mantissa:X16} ST3.E=0x{ST3.Exponent:X4} " + - // $"ST4.M=0x{ST4.Mantissa:X16} ST4.E=0x{ST4.Exponent:X4} " + - // $"ST5.M=0x{ST5.Mantissa:X16} ST5.E=0x{ST5.Exponent:X4} " + - // $"ST6.M=0x{ST6.Mantissa:X16} ST6.E=0x{ST6.Exponent:X4} " + - // $"ST7.M=0x{ST7.Mantissa:X16} ST7.E=0x{ST7.Exponent:X4} " + - // $"Cr0NpxState=0x{Cr0NpxState:X8} " + - // Segment registers - $"Gs=0x{Gs:X4} Fs=0x{Fs:X4} Es=0x{Es:X4} Ds=0x{Ds:X4} Cs=0x{Cs:X4} Ss=0x{Ss:X4} " + - // Integer registers - $"Edi=0x{Edi:X8} Esi=0x{Esi:X8} Ebx=0x{Ebx:X8} Edx=0x{Edx:X8} Ecx=0x{Ecx:X8} Eax=0x{Eax:X8} " + - // Control registers - $"Ebp=0x{Ebp:X8} Eip=0x{Eip:X8} EFlags=0x{EFlags:X8} Esp=0x{Esp:X8}"; - } - + public unsafe fixed byte ExtendedRegisters[512]; } /// diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs index 86ccd88793dea3..403efe66ec564b 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs @@ -77,12 +77,6 @@ int IXCLRDataStackWalk.GetContext(uint contextFlags, uint contextBufSize, uint* contextStruct.FillFromBuffer(contextBuf); localContextStruct.FillFromBuffer(localContextBuf); - using (StreamWriter outputFile = new StreamWriter("C:\\Users\\maxcharlamb\\OneDrive - Microsoft\\Desktop\\out.txt", true)) - { - outputFile.WriteLine($"cDAC: {contextStruct}"); - outputFile.WriteLine($" DAC: {localContextStruct}"); - } - Debug.Assert(contextStruct.Equals(localContextStruct)); } } diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataTask.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataTask.cs index 0b4539829d4563..3c189701e57fd5 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataTask.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataTask.cs @@ -54,7 +54,7 @@ int IXCLRDataTask.CreateStackWalk(uint flags, out IXCLRDataStackWalk? stackWalk) } stackWalk = new ClrDataStackWalk(_address, flags, _target, legacyStackWalk); - return HResults.E_BOUNDS; + return HResults.S_OK; } int IXCLRDataTask.GetOSThreadID(uint* id) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 6d3c470d3d649b..a6360422ce5837 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -12,7 +11,6 @@ using Microsoft.Diagnostics.DataContractReader.Contracts; using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; -using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; namespace Microsoft.Diagnostics.DataContractReader.Legacy; @@ -806,28 +804,6 @@ int ISOSDacInterface.GetMethodDescPtrFromIP(ulong ip, ulong* ppMD) { TargetPointer methodDescAddr = executionManager.GetMethodDesc(codeHandle); - // TargetCodePointer funcletStart = executionManager.GetFuncletStartAddress(codeHandle); - // bool isFunclet = executionManager.IsFunclet(codeHandle); - // TargetPointer unwindInfo = executionManager.GetUnwindInfo(codeHandle); - // executionManager.GetGCInfo(codeHandle, out TargetPointer gcInfoPtr, out uint gcVersion); - // TargetNUInt relOffset = executionManager.GetRelativeOffset(codeHandle); - // GCInfo gcInfo = new(_target, gcInfoPtr, (uint)relOffset.Value); - - // using (StreamWriter outputFile = new StreamWriter("C:\\Users\\maxcharlamb\\OneDrive - Microsoft\\Desktop\\out.txt", true)) - // { - // foreach (int offset in gcInfo.Transitions.Keys) - // { - // // outputFile.WriteLine($"CodeOffset: {offset:x8}"); - // foreach (BaseGcTransition gcTransition in gcInfo.Transitions[offset]) - // { - // // outputFile.WriteLine(gcTransition); - // } - // } - // } - // Console.WriteLine(gcInfo.Transitions); - // Console.WriteLine($"IP: {ip:x}, MethodDesc: {methodDescAddr.Value:x}, FuncletStart: {funcletStart.Value:x}, IsFunclet: {isFunclet}, UnwindInfo: {unwindInfo.Value:x}"); - // Console.WriteLine($"GCInfo: {gcInfoPtr.Value:x}, GCVersion: {gcVersion}"); - try { // Runs validation of MethodDesc From 05e59097b2519c270251384e965f15cd0eceaf8e Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Thu, 29 May 2025 12:35:26 -0400 Subject: [PATCH 16/56] use constant --- .../StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs index 5c245b3d4a963a..d9e61fa6361e98 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs @@ -46,7 +46,7 @@ public record struct InfoHdr public uint GsCookieOffset { get; set; } = 0; public uint SyncStartOffset { get; set; } = 0; public uint SyndEndOffset { get; set; } = 0; - public uint RevPInvokeOffset { get; set; } = unchecked((uint)-1); + public uint RevPInvokeOffset { get; set; } = INVALID_REV_PINVOKE_OFFSET; public uint NoGCRegionCount { get; set; } = 0; public bool HasArgTabOffset { get; set; } @@ -244,7 +244,8 @@ public static InfoHdr DecodeHeader(Target target, ref TargetPointer offset, uint infoHdr.SyncStartOffset ^= HAS_SYNC_OFFSET; break; case (byte)InfoHdrAdjust.FLIP_REV_PINVOKE_FRAME: - infoHdr.RevPInvokeOffset ^= HAS_REV_PINVOKE_FRAME_OFFSET; + infoHdr.RevPInvokeOffset = infoHdr.RevPInvokeOffset == INVALID_REV_PINVOKE_OFFSET ? + HAS_REV_PINVOKE_FRAME_OFFSET : INVALID_REV_PINVOKE_OFFSET; break; case (byte)InfoHdrAdjust.NEXT_OPCODE: From 5e813b697336ffe6d7f1735a244e7303a1cd969e Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Thu, 29 May 2025 12:35:51 -0400 Subject: [PATCH 17/56] add x86 stackwalk docs --- docs/design/datacontracts/StackWalk.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/design/datacontracts/StackWalk.md b/docs/design/datacontracts/StackWalk.md index e30cc0a013302e..43f880e997c340 100644 --- a/docs/design/datacontracts/StackWalk.md +++ b/docs/design/datacontracts/StackWalk.md @@ -331,3 +331,16 @@ TargetPointer GetFrameAddress(IStackDataFrameHandle stackDataFrameHandle); ```csharp string GetFrameName(TargetPointer frameIdentifier); ``` + +### x86 Specifics + +The x86 platform has some major differences to other platforms. In general this stems from the platform being older and not having a defined unwinding codes. Instead, to unwind managed frames, we rely on GCInfo associated with JITted code. For the unwind, we do not defer to a 'Windows like' native unwinder, instead the custom unwinder implementation was ported to managed code. + +#### GCInfo Parsing +The GCInfo structure is encoded using a variety of formats to optimize for speed of decoding and size on disk. For information on decoding and parsing refer to [GC Information Encoding for x86](../coreclr/jit/jit-gc-info-x86.md). + +#### Unwinding Algorithm + +The x86 architecture uses a custom unwinding algorithm defined in `gc_unwind_x86.inl`. The cDAC uses a copy of this algorithm ported to managed code in `X86Unwinder.cs`. + +Currently there isn't great documentation on the algorithm, beyond inspecting the implementations. From 1505bbbede7af9725995481d2e6b0b70b254534d Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Jun 2025 09:55:35 -0400 Subject: [PATCH 18/56] lazily read ArgTable --- .../Context/X86/GCInfoDecoding/GCArgTable.cs | 526 +++++++++++++++++ .../Context/X86/GCInfoDecoding/GCInfo.cs | 533 +----------------- 2 files changed, 547 insertions(+), 512 deletions(-) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs new file mode 100644 index 00000000000000..27f51252226efc --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs @@ -0,0 +1,526 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +public class GCArgTable +{ + private const uint byref_OFFSET_FLAG = 0x1; + + private readonly Target _target; + private readonly InfoHdr _header; + + public Dictionary> Transitions { get; private set; } = []; + + public GCArgTable(Target target, InfoHdr header, TargetPointer argTablePtr) + { + _target = target; + _header = header; + + TargetPointer offset = argTablePtr; + if (header.Interruptible) + { + GetTransitionsFullyInterruptible(ref offset); + } + else if (_header.EbpFrame) + { + GetTransitionsEbpFrame(ref offset); + } + else + { + GetTransitionsNoEbp(ref offset); + } + } + + private void AddNewTransition(BaseGcTransition transition) + { + if (!Transitions.TryGetValue(transition.CodeOffset, out List? value)) + { + value = []; + Transitions[transition.CodeOffset] = value; + } + + value.Add(transition); + } + + private void ArgEncoding(ref uint isPop, ref uint argOffs, ref uint argCnt, ref uint curOffs, ref bool isThis, ref bool iptr) + { + if (isPop != 0) + { + // A Pop of 0, means little-delta + + if (argOffs != 0) + { + AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt - argOffs, Action.POP, _header.EbpFrame)); + } + } + else + { + AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argOffs + 1, Action.PUSH, _header.EbpFrame, isThis, iptr)); + isThis = false; + iptr = false; + } + } + + private static RegMask ThreeBitEncodingToRegMask(byte val) => + (val & 0x7) switch + { + 0x0 => RegMask.EAX, + 0x1 => RegMask.ECX, + 0x2 => RegMask.EDX, + 0x3 => RegMask.EBX, + 0x4 => RegMask.ESP, + 0x5 => RegMask.EBP, + 0x6 => RegMask.ESI, + 0x7 => RegMask.EDI, + _ => throw new ArgumentOutOfRangeException(nameof(val), $"Not expected register value: {val}"), + }; + + private static RegMask TwoBitEncodingToRegMask(byte val) => + (val & 0x3) switch + { + 0x0 => RegMask.EDI, + 0x1 => RegMask.ESI, + 0x2 => RegMask.EBX, + 0x3 => RegMask.EBP, + _ => throw new ArgumentOutOfRangeException(nameof(val), $"Not expected register value: {val}"), + }; + + /// + /// based on GCDump::DumpGCTable + /// + private void GetTransitionsFullyInterruptible(ref TargetPointer offset) + { + uint argCnt = 0; + bool isThis = false; + bool iptr = false; + uint curOffs = 0; + + while (true) + { + uint isPop; + uint argOffs; + uint val = _target.Read(offset++); + + if ((val & 0x80) == 0) + { + /* A small 'regPtr' encoding */ + + curOffs += val & 0x7; + + Action isLive = Action.LIVE; + if ((val & 0x40) == 0) + isLive = Action.DEAD; + AddNewTransition(new GcTransitionRegister((int)curOffs, ThreeBitEncodingToRegMask((byte)((val >> 3) & 7)), isLive, isThis, iptr)); + + isThis = false; + iptr = false; + continue; + } + + /* This is probably an argument push/pop */ + + argOffs = (val & 0x38) >> 3; + + /* 6 [110] and 7 [111] are reserved for other encodings */ + + if (argOffs < 6) + { + /* A small argument encoding */ + + curOffs += val & 0x07; + isPop = val & 0x40; + + ArgEncoding(ref isPop, ref argOffs, ref argCnt, ref curOffs, ref isThis, ref iptr); + + continue; + } + else if (argOffs == 6) + { + if ((val & 0x40) != 0) + { + curOffs += (((val & 0x07) + 1) << 3); + } + else + { + // non-ptr arg push + + curOffs += (val & 0x07); + argCnt++; + AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt, Action.PUSH, _header.EbpFrame, false, false, false)); + } + + continue; + } + + // argOffs was 7 [111] which is reserved for the larger encodings + switch (val) + { + case 0xFF: + return; + case 0xBC: + isThis = true; + break; + case 0xBF: + iptr = true; + break; + case 0xB8: + val = _target.GCDecodeUnsigned(ref offset); + curOffs += val; + break; + case 0xF8: + case 0xFC: + isPop = val & 0x04; + argOffs = _target.GCDecodeUnsigned(ref offset); + ArgEncoding(ref isPop, ref argOffs, ref argCnt, ref curOffs, ref isThis, ref iptr); + break; + case 0xFD: + argOffs = _target.GCDecodeUnsigned(ref offset); + AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt, Action.KILL, _header.EbpFrame)); + break; + case 0xF9: + argOffs = _target.GCDecodeUnsigned(ref offset); + AddNewTransition(new StackDepthTransition((int)curOffs, (int)argOffs)); + argCnt += argOffs; + break; + default: + throw new BadImageFormatException($"Unexpected special code {val}"); + } + } + } + + /// + /// based on GCDump::DumpGCTable + /// + private void GetTransitionsEbpFrame(ref TargetPointer offset) + { + while (true) + { + uint argMask = 0, byrefArgMask = 0; + uint regMask, byrefRegMask = 0; + + uint argCnt = 0; + TargetPointer argOffset = offset; + uint argTabSize; + + uint val, nxt; + uint curOffs = 0; + + // Get the next byte and check for a 'special' entry + uint encType = _target.Read(offset++); + GcTransitionCall? transition; + + switch (encType) + { + default: + // A tiny or small call entry + val = encType; + + if ((val & 0x80) == 0x00) + { + if ((val & 0x0F) != 0) + { + // A tiny call entry + + curOffs += (val & 0x0F); + regMask = (val & 0x70) >> 4; + argMask = 0; + } + else + { + RegMask reg; + if ((val & 0x10) != 0) + reg = RegMask.EDI; + else if ((val & 0x20) != 0) + reg = RegMask.ESI; + else if ((val & 0x40) != 0) + reg = RegMask.EBX; + else + throw new BadImageFormatException("Invalid register"); + transition = new GcTransitionCall((int)curOffs); + transition.CallRegisters.Add(new GcTransitionCall.CallRegister(reg, false)); + AddNewTransition(transition); + + continue; + } + } + else + { + // A small call entry + curOffs += (val & 0x7F); + val = _target.Read(offset++); + regMask = val >> 5; + argMask = val & 0x1F; + } + break; + + case 0xFD: // medium encoding + argMask = _target.Read(offset++); + val = _target.Read(offset++); + argMask |= (val & 0xF0) << 4; + nxt = _target.Read(offset++); + curOffs += (val & 0x0F) + ((nxt & 0x1F) << 4); + regMask = nxt >> 5; // EBX,ESI,EDI + break; + + case 0xF9: // medium encoding with byrefs + curOffs += _target.Read(offset++); + val = _target.Read(offset++); + argMask = val & 0x1F; + regMask = val >> 5; + val = _target.Read(offset++); + byrefArgMask = val & 0x1F; + byrefRegMask = val >> 5; + break; + case 0xFE: // large encoding + case 0xFA: // large encoding with byrefs + val = _target.Read(offset++); + regMask = val & 0x7; + byrefRegMask = val >> 4; + + curOffs += _target.Read(offset); + offset += 4; + argMask = _target.Read(offset); + offset += 4; + + if (encType == 0xFA) // read byrefArgMask + { + byrefArgMask = _target.Read(offset); + offset += 4; + } + break; + case 0xFB: // huge encoding + val = _target.Read(offset++); + regMask = val & 0x7; + byrefRegMask = val >> 4; + curOffs = _target.Read(offset); + offset += 4; + argCnt = _target.Read(offset); + offset += 4; + argTabSize = _target.Read(offset); + offset += 4; + argOffset = offset; + offset += argTabSize; + break; + case 0xFF: + return; + } + + /* + Here we have the following values: + + curOffs ... the code offset of the call + regMask ... mask of live pointer register variables + argMask ... bitmask of pushed pointer arguments + byrefRegMask ... byref qualifier for regMask + byrefArgMask ... byrer qualifier for argMask + */ + transition = new GcTransitionCall((int)curOffs, _header.EbpFrame, regMask, byrefRegMask); + AddNewTransition(transition); + + if (argCnt != 0) + { + do + { + val = _target.GCDecodeUnsigned(ref argOffset); + + uint stkOffs = val & ~byref_OFFSET_FLAG; + uint lowBit = val & byref_OFFSET_FLAG; + transition.PtrArgs.Add(new GcTransitionCall.PtrArg(stkOffs, lowBit)); + } + while (--argCnt > 0); + } + else + { + transition.ArgMask = argMask; + if (byrefArgMask != 0) + transition.IArgs = byrefArgMask; + } + } + } + + /// + /// based on GCDump::DumpGCTable + /// + private void SaveCallTransition(ref TargetPointer offset, uint val, uint curOffs, uint callRegMask, bool callPndTab, uint callPndTabCnt, uint callPndMask, uint lastSkip, ref uint imask) + { + uint iregMask, iargMask; + iregMask = imask & 0xF; + iargMask = imask >> 4; + + GcTransitionCall transition = new GcTransitionCall((int)curOffs, _header.EbpFrame, callRegMask, iregMask); + AddNewTransition(transition); + + if (callPndTab) + { + for (int i = 0; i < callPndTabCnt; i++) + { + uint pndOffs = _target.GCDecodeUnsigned(ref offset); + + uint stkOffs = val & ~byref_OFFSET_FLAG; + uint lowBit = val & byref_OFFSET_FLAG; + Console.WriteLine($"stkOffs: {stkOffs}, lowBit: {lowBit}"); + + transition.PtrArgs.Add(new GcTransitionCall.PtrArg(pndOffs, 0)); + } + } + else + { + if (callPndMask != 0) + transition.ArgMask = callPndMask; + if (iargMask != 0) + transition.IArgs = iargMask; + } + + Console.WriteLine($"lastSkip: {lastSkip}"); + imask /* = lastSkip */ = 0; + } + + private void GetTransitionsNoEbp(ref TargetPointer offset) + { + uint curOffs = 0; + uint lastSkip = 0; + uint imask = 0; + + for (; ; ) + { + uint val = _target.Read(offset++); + + if ((val & 0x80) == 0) + { + if ((val & 0x40) == 0) + { + if ((val & 0x20) == 0) + { + // push 000DDDDD push one item, 5-bit delta + curOffs += val & 0x1F; + AddNewTransition(new GcTransitionRegister((int)curOffs, RegMask.ESP, Action.PUSH)); + } + else + { + // push 00100000 [pushCount] ESP push multiple items + uint pushCount = _target.GCDecodeUnsigned(ref offset); + AddNewTransition(new GcTransitionRegister((int)curOffs, RegMask.ESP, Action.PUSH, false, false, (int)pushCount)); + } + } + else + { + uint popSize; + uint skip; + + if ((val & 0x3f) == 0) + { + // + // skip 01000000 [Delta] Skip arbitrary sized delta + // + skip = _target.GCDecodeUnsigned(ref offset); + curOffs += skip; + lastSkip = skip; + } + else + { + // pop 01CCDDDD pop CC items, 4-bit delta + popSize = (val & 0x30) >> 4; + skip = val & 0x0f; + curOffs += skip; + + if (popSize > 0) + { + AddNewTransition(new GcTransitionRegister((int)curOffs, RegMask.ESP, Action.POP, false, false, (int)popSize)); + } + else + lastSkip = skip; + } + } + } + else + { + uint callArgCnt = 0; + uint callRegMask; + bool callPndTab = false; + uint callPndMask = 0; + uint callPndTabCnt = 0, callPndTabSize = 0; + + switch ((val & 0x70) >> 4) + { + default: + // + // call 1PPPPPPP Call Pattern, P=[0..79] + // + CallPattern.DecodeCallPattern((val & 0x7f), out callArgCnt, out callRegMask, out callPndMask, out lastSkip); + curOffs += lastSkip; + SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); + break; + + case 5: + // + // call 1101RRRR DDCCCMMM Call RegMask=RRRR,ArgCnt=CCC, + // ArgMask=MMM Delta=commonDelta[DD] + // + callRegMask = val & 0xf; // EBP,EBX,ESI,EDI + val = _target.Read(offset++); + callPndMask = val & 0x7; + callArgCnt = (val >> 3) & 0x7; + lastSkip = CallPattern.callCommonDelta[val >> 6]; + curOffs += lastSkip; + SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); + break; + case 6: + // + // call 1110RRRR [ArgCnt] [ArgMask] + // Call ArgCnt,RegMask=RRR,ArgMask + // + callRegMask = val & 0xf; // EBP,EBX,ESI,EDI + callArgCnt = _target.GCDecodeUnsigned(ref offset); + callPndMask = _target.GCDecodeUnsigned(ref offset); + SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); + break; + case 7: + switch (val & 0x0C) + { + case 0x00: + // iptr 11110000 [IPtrMask] Arbitrary Interior Pointer Mask + imask = _target.GCDecodeUnsigned(ref offset); + AddNewTransition(new IPtrMask((int)curOffs, imask)); + break; + + case 0x04: + AddNewTransition(new CalleeSavedRegister((int)curOffs, TwoBitEncodingToRegMask((byte)(val & 0x3)))); + break; + + case 0x08: + val = _target.Read(offset++); + callRegMask = val & 0xF; + imask = val >> 4; + lastSkip = _target.Read(offset); + offset += 4; + curOffs += lastSkip; + callArgCnt = _target.Read(offset); + offset += 4; + callPndTabCnt = _target.Read(offset); + offset += 4; + callPndTabSize = _target.Read(offset); + offset += 4; + callPndTab = true; + SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); + AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); + break; + case 0x0C: + return; + default: + throw new BadImageFormatException("Invalid GC encoding"); + } + break; + } + Console.WriteLine($"CallArgCount: {callArgCnt}"); + Console.WriteLine($"CallPndTabCnt: {callPndTabSize}"); + } + } + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs index aa07167f8e5089..c68cdd2dc4d61c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs @@ -29,8 +29,6 @@ public enum RegMask public record GCInfo { - private const uint byref_OFFSET_FLAG = 0x1; - private readonly Target _target; public uint RelativeOffset { get; set; } @@ -57,10 +55,13 @@ public record GCInfo public NoGcRegionTable NoGCRegions { get; set; } = null!; public GcSlotTable SlotTable { get; set; } = null!; - public Dictionary> Transitions { get; set; } = []; + + private Lazy>> _transitions = new(); + public Dictionary> Transitions => _transitions.Value; // Number of bytes of stack space that has been pushed for arguments at the current RelativeOffset. - public uint PushedArgSize { get; set; } + private Lazy _pushedArgSize; + public uint PushedArgSize => _pushedArgSize.Value; public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) { @@ -128,30 +129,27 @@ public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) SavedRegsCountExclFP--; } - NoGCRegions = new NoGcRegionTable(target, Header, ref offset); + // NoGCRegions = new NoGcRegionTable(target, Header, ref offset); - SlotTable = new GcSlotTable(target, Header, ref offset); + // SlotTable = new GcSlotTable(target, Header, ref offset); // Verify the argument table offset is consistent - Debug.Assert(!Header.HasArgTabOffset || offset.Value == gcInfoAddress.Value + infoHdrSize + Header.ArgTabOffset); - Transitions = new Dictionary>(); - if (Header.Interruptible) - { - GetTransitionsFullyInterruptible(ref offset); - } - else if (Header.EbpFrame) - { - GetTransitionsEbpFrame(ref offset); - } - else - { - GetTransitionsNoEbp(ref offset); - } + // Debug.Assert(!Header.HasArgTabOffset || offset.Value == gcInfoAddress.Value + infoHdrSize + Header.ArgTabOffset); - CalculateDepth(); + // Lazily initialize transitions. These are not present in all Heap dumps, only if they are required for stack walking. + _transitions = new(() => + { + // calculate the Argument Table pointer + TargetPointer argTabPtr = gcInfoAddress.Value + infoHdrSize + Header.ArgTabOffset; + GCArgTable argTable = new(_target, Header, argTabPtr); + return argTable.Transitions; + }); + + // Lazily initialize the pushed argument size. This relies on the transitions being calculated first. + _pushedArgSize = new(CalculateDepth); } - private void CalculateDepth() + private uint CalculateDepth() { using StreamWriter outputFile = new StreamWriter("C:\\Users\\maxcharlamb\\OneDrive - Microsoft\\Desktop\\out.txt", true); @@ -203,496 +201,7 @@ private void CalculateDepth() outputFile.WriteLine($"depth: {depth}"); } - PushedArgSize = (uint)(depth * _target.PointerSize); - } - - private void AddNewTransition(BaseGcTransition transition) - { - if (!Transitions.TryGetValue(transition.CodeOffset, out List? value)) - { - value = []; - Transitions[transition.CodeOffset] = value; - } - - value.Add(transition); - } - - private void ArgEncoding(ref uint isPop, ref uint argOffs, ref uint argCnt, ref uint curOffs, ref bool isThis, ref bool iptr) - { - if (isPop != 0) - { - // A Pop of 0, means little-delta - - if (argOffs != 0) - { - AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt - argOffs, Action.POP, Header.EbpFrame)); - } - } - else - { - AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argOffs + 1, Action.PUSH, Header.EbpFrame, isThis, iptr)); - isThis = false; - iptr = false; - } - } - - private static RegMask ThreeBitEncodingToRegMask(byte val) => - (val & 0x7) switch - { - 0x0 => RegMask.EAX, - 0x1 => RegMask.ECX, - 0x2 => RegMask.EDX, - 0x3 => RegMask.EBX, - 0x4 => RegMask.ESP, - 0x5 => RegMask.EBP, - 0x6 => RegMask.ESI, - 0x7 => RegMask.EDI, - _ => throw new ArgumentOutOfRangeException(nameof(val), $"Not expected register value: {val}"), - }; - - private static RegMask TwoBitEncodingToRegMask(byte val) => - (val & 0x3) switch - { - 0x0 => RegMask.EDI, - 0x1 => RegMask.ESI, - 0x2 => RegMask.EBX, - 0x3 => RegMask.EBP, - _ => throw new ArgumentOutOfRangeException(nameof(val), $"Not expected register value: {val}"), - }; - - /// - /// based on GCDump::DumpGCTable - /// - private void GetTransitionsFullyInterruptible(ref TargetPointer offset) - { - uint argCnt = 0; - bool isThis = false; - bool iptr = false; - uint curOffs = 0; - - while (true) - { - uint isPop; - uint argOffs; - uint val = _target.Read(offset++); - - if ((val & 0x80) == 0) - { - /* A small 'regPtr' encoding */ - - curOffs += val & 0x7; - - Action isLive = Action.LIVE; - if ((val & 0x40) == 0) - isLive = Action.DEAD; - AddNewTransition(new GcTransitionRegister((int)curOffs, ThreeBitEncodingToRegMask((byte)((val >> 3) & 7)), isLive, isThis, iptr)); - - isThis = false; - iptr = false; - continue; - } - - /* This is probably an argument push/pop */ - - argOffs = (val & 0x38) >> 3; - - /* 6 [110] and 7 [111] are reserved for other encodings */ - - if (argOffs < 6) - { - /* A small argument encoding */ - - curOffs += val & 0x07; - isPop = val & 0x40; - - ArgEncoding(ref isPop, ref argOffs, ref argCnt, ref curOffs, ref isThis, ref iptr); - - continue; - } - else if (argOffs == 6) - { - if ((val & 0x40) != 0) - { - curOffs += (((val & 0x07) + 1) << 3); - } - else - { - // non-ptr arg push - - curOffs += (val & 0x07); - argCnt++; - AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt, Action.PUSH, Header.EbpFrame, false, false, false)); - } - - continue; - } - - // argOffs was 7 [111] which is reserved for the larger encodings - switch (val) - { - case 0xFF: - return; - case 0xBC: - isThis = true; - break; - case 0xBF: - iptr = true; - break; - case 0xB8: - val = _target.GCDecodeUnsigned(ref offset); - curOffs += val; - break; - case 0xF8: - case 0xFC: - isPop = val & 0x04; - argOffs = _target.GCDecodeUnsigned(ref offset); - ArgEncoding(ref isPop, ref argOffs, ref argCnt, ref curOffs, ref isThis, ref iptr); - break; - case 0xFD: - argOffs = _target.GCDecodeUnsigned(ref offset); - AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt, Action.KILL, Header.EbpFrame)); - break; - case 0xF9: - argOffs = _target.GCDecodeUnsigned(ref offset); - AddNewTransition(new StackDepthTransition((int)curOffs, (int)argOffs)); - argCnt += argOffs; - break; - default: - throw new BadImageFormatException($"Unexpected special code {val}"); - } - } - } - - /// - /// based on GCDump::DumpGCTable - /// - private void GetTransitionsEbpFrame(ref TargetPointer offset) - { - while (true) - { - uint argMask = 0, byrefArgMask = 0; - uint regMask, byrefRegMask = 0; - - uint argCnt = 0; - TargetPointer argOffset = offset; - uint argTabSize; - - uint val, nxt; - uint curOffs = 0; - - // Get the next byte and check for a 'special' entry - uint encType = _target.Read(offset++); - GcTransitionCall? transition; - - switch (encType) - { - default: - // A tiny or small call entry - val = encType; - - if ((val & 0x80) == 0x00) - { - if ((val & 0x0F) != 0) - { - // A tiny call entry - - curOffs += (val & 0x0F); - regMask = (val & 0x70) >> 4; - argMask = 0; - } - else - { - RegMask reg; - if ((val & 0x10) != 0) - reg = RegMask.EDI; - else if ((val & 0x20) != 0) - reg = RegMask.ESI; - else if ((val & 0x40) != 0) - reg = RegMask.EBX; - else - throw new BadImageFormatException("Invalid register"); - transition = new GcTransitionCall((int)curOffs); - transition.CallRegisters.Add(new GcTransitionCall.CallRegister(reg, false)); - AddNewTransition(transition); - - continue; - } - } - else - { - // A small call entry - curOffs += (val & 0x7F); - val = _target.Read(offset++); - regMask = val >> 5; - argMask = val & 0x1F; - } - break; - - case 0xFD: // medium encoding - argMask = _target.Read(offset++); - val = _target.Read(offset++); - argMask |= (val & 0xF0) << 4; - nxt = _target.Read(offset++); - curOffs += (val & 0x0F) + ((nxt & 0x1F) << 4); - regMask = nxt >> 5; // EBX,ESI,EDI - break; - - case 0xF9: // medium encoding with byrefs - curOffs += _target.Read(offset++); - val = _target.Read(offset++); - argMask = val & 0x1F; - regMask = val >> 5; - val = _target.Read(offset++); - byrefArgMask = val & 0x1F; - byrefRegMask = val >> 5; - break; - case 0xFE: // large encoding - case 0xFA: // large encoding with byrefs - val = _target.Read(offset++); - regMask = val & 0x7; - byrefRegMask = val >> 4; - - curOffs += _target.Read(offset); - offset += 4; - argMask = _target.Read(offset); - offset += 4; - - if (encType == 0xFA) // read byrefArgMask - { - byrefArgMask = _target.Read(offset); - offset += 4; - } - break; - case 0xFB: // huge encoding - val = _target.Read(offset++); - regMask = val & 0x7; - byrefRegMask = val >> 4; - curOffs = _target.Read(offset); - offset += 4; - argCnt = _target.Read(offset); - offset += 4; - argTabSize = _target.Read(offset); - offset += 4; - argOffset = offset; - offset += argTabSize; - break; - case 0xFF: - return; - } - - /* - Here we have the following values: - - curOffs ... the code offset of the call - regMask ... mask of live pointer register variables - argMask ... bitmask of pushed pointer arguments - byrefRegMask ... byref qualifier for regMask - byrefArgMask ... byrer qualifier for argMask - */ - transition = new GcTransitionCall((int)curOffs, Header.EbpFrame, regMask, byrefRegMask); - AddNewTransition(transition); - - if (argCnt != 0) - { - do - { - val = _target.GCDecodeUnsigned(ref argOffset); - - uint stkOffs = val & ~byref_OFFSET_FLAG; - uint lowBit = val & byref_OFFSET_FLAG; - transition.PtrArgs.Add(new GcTransitionCall.PtrArg(stkOffs, lowBit)); - } - while (--argCnt > 0); - } - else - { - transition.ArgMask = argMask; - if (byrefArgMask != 0) - transition.IArgs = byrefArgMask; - } - } - } - - /// - /// based on GCDump::DumpGCTable - /// - private void SaveCallTransition(ref TargetPointer offset, uint val, uint curOffs, uint callRegMask, bool callPndTab, uint callPndTabCnt, uint callPndMask, uint lastSkip, ref uint imask) - { - uint iregMask, iargMask; - iregMask = imask & 0xF; - iargMask = imask >> 4; - - GcTransitionCall transition = new GcTransitionCall((int)curOffs, Header.EbpFrame, callRegMask, iregMask); - AddNewTransition(transition); - - if (callPndTab) - { - for (int i = 0; i < callPndTabCnt; i++) - { - uint pndOffs = _target.GCDecodeUnsigned(ref offset); - - uint stkOffs = val & ~byref_OFFSET_FLAG; - uint lowBit = val & byref_OFFSET_FLAG; - Console.WriteLine($"stkOffs: {stkOffs}, lowBit: {lowBit}"); - - transition.PtrArgs.Add(new GcTransitionCall.PtrArg(pndOffs, 0)); - } - } - else - { - if (callPndMask != 0) - transition.ArgMask = callPndMask; - if (iargMask != 0) - transition.IArgs = iargMask; - } - - Console.WriteLine($"lastSkip: {lastSkip}"); - imask /* = lastSkip */ = 0; - } - - private void GetTransitionsNoEbp(ref TargetPointer offset) - { - uint curOffs = 0; - uint lastSkip = 0; - uint imask = 0; - - for (; ; ) - { - uint val = _target.Read(offset++); - - if ((val & 0x80) == 0) - { - if ((val & 0x40) == 0) - { - if ((val & 0x20) == 0) - { - // push 000DDDDD push one item, 5-bit delta - curOffs += val & 0x1F; - AddNewTransition(new GcTransitionRegister((int)curOffs, RegMask.ESP, Action.PUSH)); - } - else - { - // push 00100000 [pushCount] ESP push multiple items - uint pushCount = _target.GCDecodeUnsigned(ref offset); - AddNewTransition(new GcTransitionRegister((int)curOffs, RegMask.ESP, Action.PUSH, false, false, (int)pushCount)); - } - } - else - { - uint popSize; - uint skip; - - if ((val & 0x3f) == 0) - { - // - // skip 01000000 [Delta] Skip arbitrary sized delta - // - skip = _target.GCDecodeUnsigned(ref offset); - curOffs += skip; - lastSkip = skip; - } - else - { - // pop 01CCDDDD pop CC items, 4-bit delta - popSize = (val & 0x30) >> 4; - skip = val & 0x0f; - curOffs += skip; - - if (popSize > 0) - { - AddNewTransition(new GcTransitionRegister((int)curOffs, RegMask.ESP, Action.POP, false, false, (int)popSize)); - } - else - lastSkip = skip; - } - } - } - else - { - uint callArgCnt = 0; - uint callRegMask; - bool callPndTab = false; - uint callPndMask = 0; - uint callPndTabCnt = 0, callPndTabSize = 0; - - switch ((val & 0x70) >> 4) - { - default: - // - // call 1PPPPPPP Call Pattern, P=[0..79] - // - CallPattern.DecodeCallPattern((val & 0x7f), out callArgCnt, out callRegMask, out callPndMask, out lastSkip); - curOffs += lastSkip; - SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); - AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); - break; - - case 5: - // - // call 1101RRRR DDCCCMMM Call RegMask=RRRR,ArgCnt=CCC, - // ArgMask=MMM Delta=commonDelta[DD] - // - callRegMask = val & 0xf; // EBP,EBX,ESI,EDI - val = _target.Read(offset++); - callPndMask = val & 0x7; - callArgCnt = (val >> 3) & 0x7; - lastSkip = CallPattern.callCommonDelta[val >> 6]; - curOffs += lastSkip; - SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); - AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); - break; - case 6: - // - // call 1110RRRR [ArgCnt] [ArgMask] - // Call ArgCnt,RegMask=RRR,ArgMask - // - callRegMask = val & 0xf; // EBP,EBX,ESI,EDI - callArgCnt = _target.GCDecodeUnsigned(ref offset); - callPndMask = _target.GCDecodeUnsigned(ref offset); - SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); - AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); - break; - case 7: - switch (val & 0x0C) - { - case 0x00: - // iptr 11110000 [IPtrMask] Arbitrary Interior Pointer Mask - imask = _target.GCDecodeUnsigned(ref offset); - AddNewTransition(new IPtrMask((int)curOffs, imask)); - break; - - case 0x04: - AddNewTransition(new CalleeSavedRegister((int)curOffs, TwoBitEncodingToRegMask((byte)(val & 0x3)))); - break; - - case 0x08: - val = _target.Read(offset++); - callRegMask = val & 0xF; - imask = val >> 4; - lastSkip = _target.Read(offset); - offset += 4; - curOffs += lastSkip; - callArgCnt = _target.Read(offset); - offset += 4; - callPndTabCnt = _target.Read(offset); - offset += 4; - callPndTabSize = _target.Read(offset); - offset += 4; - callPndTab = true; - SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); - AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); - break; - case 0x0C: - return; - default: - throw new BadImageFormatException("Invalid GC encoding"); - } - break; - } - Console.WriteLine($"CallArgCount: {callArgCnt}"); - Console.WriteLine($"CallPndTabCnt: {callPndTabSize}"); - } - } + return (uint)(depth * _target.PointerSize); } } From e648dda354f552dd5b9368533db590b39b8a06cc Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Jun 2025 11:02:52 -0400 Subject: [PATCH 19/56] add datadescriptors for CalleeSavedRegisters on x86 --- src/coreclr/debug/runtimeinfo/datadescriptor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index ce914e14fa4ebf..38dd82c9efc793 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -784,7 +784,7 @@ CDAC_TYPE_END(FaultingExceptionFrame) // CalleeSavedRegisters struct is different on each platform CDAC_TYPE_BEGIN(CalleeSavedRegisters) CDAC_TYPE_SIZE(sizeof(CalleeSavedRegisters)) -#if defined(TARGET_AMD64) +#if defined(TARGET_AMD64) || defined(TARGET_X86) #define CALLEE_SAVED_REGISTER(regname) \ CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, regname, offsetof(CalleeSavedRegisters, regname)) From 2335f6dbea7545d53d3785a3c204647869b37624 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Jun 2025 11:03:30 -0400 Subject: [PATCH 20/56] remove unused GCInfo decoders --- .../Context/X86/GCInfoDecoding/GCInfo.cs | 10 - .../Context/X86/GCInfoDecoding/GCSlotTable.cs | 196 ------------------ .../X86/GCInfoDecoding/NoGcRegionTable.cs | 60 ------ 3 files changed, 266 deletions(-) delete mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCSlotTable.cs delete mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/NoGcRegionTable.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs index c68cdd2dc4d61c..ead11f8e8d5e27 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs @@ -53,9 +53,6 @@ public record GCInfo public uint SavedRegsCountExclFP { get; set; } public RegMask SavedRegsMask { get; set; } = RegMask.NONE; - public NoGcRegionTable NoGCRegions { get; set; } = null!; - public GcSlotTable SlotTable { get; set; } = null!; - private Lazy>> _transitions = new(); public Dictionary> Transitions => _transitions.Value; @@ -129,13 +126,6 @@ public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) SavedRegsCountExclFP--; } - // NoGCRegions = new NoGcRegionTable(target, Header, ref offset); - - // SlotTable = new GcSlotTable(target, Header, ref offset); - - // Verify the argument table offset is consistent - // Debug.Assert(!Header.HasArgTabOffset || offset.Value == gcInfoAddress.Value + infoHdrSize + Header.ArgTabOffset); - // Lazily initialize transitions. These are not present in all Heap dumps, only if they are required for stack walking. _transitions = new(() => { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCSlotTable.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCSlotTable.cs deleted file mode 100644 index a70a94a83de60c..00000000000000 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCSlotTable.cs +++ /dev/null @@ -1,196 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Reflection.PortableExecutable; -using System.Text; - -namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; - -public class GcSlotTable -{ - [Flags] - public enum GcSlotFlags - { - GC_SLOT_BASE = 0x0, - GC_SLOT_INTERIOR = 0x1, - GC_SLOT_PINNED = 0x2, - GC_SLOT_UNTRACKED = 0x4, - - GC_SLOT_INVALID = -1 - }; - - public class GcSlot - { - public int Index { get; set; } - public string Register { get; set; } - public int StackOffset { get; set; } - public int LowBits { get; set; } - public GcSlotFlags Flags { get; set; } - - public int BeginOffset { get; set; } - public int EndOffset { get; set; } - - public GcSlot(int index, string reg, int stkOffs, int lowBits, GcSlotFlags flags) - { - Index = index; - Register = reg; - StackOffset = stkOffs; - LowBits = lowBits; - Flags = flags; - - BeginOffset = -1; - EndOffset = -1; - } - - public GcSlot(int index, string reg, int beginOffs, int endOffs, int varOffs, int lowBits, GcSlotFlags flags) - { - Index = index; - Register = $"E{reg}P"; - StackOffset = varOffs; - LowBits = lowBits; - Flags = flags; - - BeginOffset = beginOffs; - EndOffset = endOffs; - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - - if ((Flags & GcSlotFlags.GC_SLOT_UNTRACKED) != 0) - { - if (StackOffset < 0) - { - sb.AppendLine($" [{Register}-{-StackOffset}]"); - } - else - { - sb.AppendLine($" [{Register}+{StackOffset}]"); - } - } - else - { - sb.AppendLine($" BeginOffset: {BeginOffset}"); - sb.AppendLine($" EndOffset: {EndOffset}"); - if (Register.Equals("BP")) - { - sb.AppendLine($" [{Register}-{-StackOffset}]"); - } - else - { - sb.AppendLine($" [{Register}+{-StackOffset}]"); - } - } - - sb.AppendLine($" Flags: {Flags}"); - - sb.Append($" LowBits: "); - if ((Flags & GcSlotFlags.GC_SLOT_UNTRACKED) != 0) - { - if ((LowBits & pinned_OFFSET_FLAG) != 0) sb.Append("pinned "); - if ((LowBits & byref_OFFSET_FLAG) != 0) sb.Append("byref "); - } - sb.AppendLine(); - - return sb.ToString(); - } - } - - private const uint OFFSET_MASK = 0x3; - private const uint byref_OFFSET_FLAG = 0x1; // the offset is an interior ptr - private const uint pinned_OFFSET_FLAG = 0x2; // the offset is a pinned ptr - - public List GcSlots { get; set; } - - - public GcSlotTable(Target target, InfoHdr header, ref TargetPointer offset) - { - GcSlots = new List(); - - DecodeUntracked(target, header, ref offset); - DecodeFrameVariableLifetimeTable(target, header, ref offset); - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - - sb.AppendLine($" GcSlots:"); - sb.AppendLine($" -------------------------"); - foreach (GcSlot slot in GcSlots) - { - sb.Append(slot.ToString()); - sb.AppendLine($" -------------------------"); - } - - return sb.ToString(); - } - - /// - /// based on GCDump::DumpGCTable - /// - private void DecodeUntracked(Target target, InfoHdr header, ref TargetPointer offset) - { - uint calleeSavedRegs = 0; - if (header.DoubleAlign) - { - calleeSavedRegs = 0; - if (header.EdiSaved) calleeSavedRegs++; - if (header.EsiSaved) calleeSavedRegs++; - if (header.EbxSaved) calleeSavedRegs++; - } - - uint count = header.UntrackedCount; - int lastStkOffs = 0; - while (count-- > 0) - { - int stkOffsDelta; - int lowBits; - - char reg = header.EbpFrame ? 'B' : 'S'; - - stkOffsDelta = target.GCDecodeSigned(ref offset); - int stkOffs = lastStkOffs - stkOffsDelta; - lastStkOffs = stkOffs; - - lowBits = (int)OFFSET_MASK & stkOffs; - stkOffs = (int)((uint)stkOffs & ~OFFSET_MASK); - - if (header.DoubleAlign && - (uint)stkOffs >= sizeof(int) * (header.FrameSize + calleeSavedRegs)) - { - reg = 'B'; - stkOffs -= sizeof(int) * (int)(header.FrameSize + calleeSavedRegs); - } - - GcSlots.Add(new GcSlot(GcSlots.Count, $"E{reg}P", stkOffs, lowBits, GcSlotFlags.GC_SLOT_UNTRACKED)); - } - } - - /// - /// based on GCDump::DumpGCTable - /// - private void DecodeFrameVariableLifetimeTable(Target target, InfoHdr header, ref TargetPointer offset) - { - uint count = header.VarPtrTableSize; - uint curOffs = 0; - while (count-- > 0) - { - uint varOffs = target.GCDecodeUnsigned(ref offset); - uint begOffs = target.GCDecodeUDelta(ref offset, curOffs); - uint endOffs = target.GCDecodeUDelta(ref offset, begOffs); - - - uint lowBits = varOffs & 0x3; - varOffs &= ~OFFSET_MASK; - - curOffs = begOffs; - - string reg = header.EbpFrame ? "BP" : "SP"; - GcSlots.Add(new GcSlot(GcSlots.Count, reg, (int)begOffs, (int)endOffs, (int)varOffs, (int)lowBits, GcSlotFlags.GC_SLOT_BASE)); - } - } -} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/NoGcRegionTable.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/NoGcRegionTable.cs deleted file mode 100644 index 909c5b5b9f8979..00000000000000 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/NoGcRegionTable.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Text; - -namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; - -public class NoGcRegionTable -{ - public class NoGcRegion - { - public uint Offset { get; set; } - public uint Size { get; set; } - - public NoGcRegion(uint offset, uint size) - { - Offset = offset; - Size = size; - } - - public override string ToString() - { - return $" [{Offset:04X}-{Offset+Size:04X})\n"; - } - } - - public List Regions { get; set; } - - public NoGcRegionTable(Target target, InfoHdr header, ref TargetPointer offset) - { - Regions = new List((int)header.NoGCRegionCount); - - uint count = header.NoGCRegionCount; - while (count-- > 0) - { - uint regionOffset = target.GCDecodeUnsigned(ref offset); - uint regionSize = target.GCDecodeUnsigned(ref offset); - Regions.Add(new NoGcRegion(regionOffset, regionSize)); - } - } - - public override string ToString() - { - if (Regions.Count > 0) - { - StringBuilder sb = new StringBuilder(); - - sb.AppendLine($" No GC regions:"); - foreach (NoGcRegion region in Regions) - { - sb.Append(region.ToString()); - } - - return sb.ToString(); - } - - return string.Empty; - } -} From 7f06960995863320785ca367c3afda67d5fa2c87 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Jun 2025 11:35:45 -0400 Subject: [PATCH 21/56] fix stackwalk logic for x86 --- .../Contracts/IRuntimeTypeSystem.cs | 4 ++ .../Contracts/RuntimeTypeSystem_1.cs | 27 +++++++++++++ .../StackWalk/FrameHandling/FrameIterator.cs | 4 +- .../Contracts/StackWalk/StackWalk_1.cs | 38 +++++++++++++++---- 4 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index 61c4274fc763e5..522a50f06d9411 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -156,6 +156,10 @@ public interface IRuntimeTypeSystem : IContract // A IL Stub method is also a StoredSigMethodDesc, and a NoMetadataMethod bool IsILStub(MethodDescHandle methodDesc) => throw new NotImplementedException(); + // Return true if the MethodDesc represents an IL Stub that takes a context argument + // that is an interop MethodDesc. + public bool HasMDContextArg(MethodDescHandle methodDescHandle) => throw new NotImplementedException(); + bool IsCollectibleMethod(MethodDescHandle methodDesc) => throw new NotImplementedException(); bool IsVersionable(MethodDescHandle methodDesc) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index 1225c281df73ee..45e4def7a75583 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -76,8 +76,14 @@ internal enum InstantiatedMethodDescFlags2 : ushort [Flags] internal enum DynamicMethodDescExtendedFlags : uint { + Static = 0x00001000, IsLCGMethod = 0x00004000, IsILStub = 0x00008000, + IsDelegate = 0x00010000, + IsCALLI = 0x00020000, + FlagMask = 0x0003f800, + StackArgSizeMask = 0xfffc0000, + ILStubTypeMask = ~(FlagMask | StackArgSizeMask), } // on MethodDescChunk.FlagsAndTokenRange @@ -229,9 +235,18 @@ private DynamicMethodDesc(Target target, TargetPointer methodDescPointer) public string MethodName { get; } public DynamicMethodDescExtendedFlags ExtendedFlags => (DynamicMethodDescExtendedFlags)_storedSigDesc.ExtendedFlags; + public uint ILStubType => (uint)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask); + public bool IsStatic => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.Static); public bool IsDynamicMethod => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsLCGMethod); public bool IsILStub => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsILStub); + public bool IsDelegate => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsDelegate); + public bool IsCALLI => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsCALLI); + + public bool IsPInvokeStub => IsStatic && !IsCALLI && ILStubType == 0x1; + public bool IsCLRToCOMStub => !IsStatic && ILStubType == 0x2; + + public bool HasMDContextArg => IsCLRToCOMStub || (IsPInvokeStub && !IsDelegate); } private sealed class StoredSigMethodDesc : IData @@ -787,6 +802,18 @@ public bool IsILStub(MethodDescHandle methodDescHandle) return AsDynamicMethodDesc(methodDesc).IsILStub; } + public bool HasMDContextArg(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodClassification.Dynamic) + { + return false; + } + + return AsDynamicMethodDesc(methodDesc).HasMDContextArg; + } + private MethodTable GetOrCreateMethodTable(MethodDesc methodDesc) { // Ensures that the method table is valid, created, and cached diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs index 5e7774e39d507e..885d952c62afe4 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs @@ -7,7 +7,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; internal sealed class FrameIterator { - private enum FrameType + internal enum FrameType { Unknown, @@ -157,6 +157,8 @@ public static string GetFrameName(Target target, TargetPointer frameIdentifier) return frameType.ToString(); } + public FrameType GetCurrentFrameType() => GetFrameType(target, CurrentFrame.Identifier); + private static FrameType GetFrameType(Target target, TargetPointer frameIdentifier) { foreach (FrameType frameType in Enum.GetValues()) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs index a23c2fd94988b4..6adef7b2f15763 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs @@ -95,7 +95,7 @@ private bool Next(StackWalkData handle) break; case StackWalkState.SW_FRAME: handle.FrameIter.UpdateContextFromFrame(handle.Context); - if (_arch == RuntimeInfoArchitecture.X86 || !handle.FrameIter.IsInlineCallFrameWithActiveCall()) + if (!handle.FrameIter.IsInlineCallFrameWithActiveCall()) { handle.FrameIter.Next(); } @@ -127,20 +127,20 @@ private void UpdateState(StackWalkData handle, IPlatformAgnosticContext prevCont // x86 Specific Checks if (_arch == RuntimeInfoArchitecture.X86) { - if (IsManaged(prevContext.InstructionPointer, out CodeBlockHandle? cbh)) + if (IsManaged(prevContext.InstructionPointer, out CodeBlockHandle? prevCbh)) { IExecutionManager eman = _target.Contracts.ExecutionManager; - if (!eman.IsFunclet(cbh.Value)) + if (!eman.IsFunclet(prevCbh.Value)) { - eman.GetGCInfo(cbh.Value, out TargetPointer gcInfoAddress, out uint _); - uint relOffset = (uint)eman.GetRelativeOffset(cbh.Value).Value; + eman.GetGCInfo(prevCbh.Value, out TargetPointer gcInfoAddress, out uint _); + uint relOffset = (uint)eman.GetRelativeOffset(prevCbh.Value).Value; GCInfo gcInfo = new(_target, gcInfoAddress, relOffset); if (gcInfo.HasReversePInvoke) { // The managed frame we've unwound from had reverse PInvoke frame. Since we are on a frameless // frame, that means that the method was called from managed code without any native frames in between. - // On x86, the InlinedCallFrame of the pinvoke would get skipped as we've just unwound to the pinvoke IL stub and + // On x86, the InlinedCallFrame of the PInvoke would get skipped as we've just unwound to the PInvoke IL stub and // for this architecture, the inlined call frames are supposed to be processed before the managed frame they are stored in. // So we force the stack frame iterator to process the InlinedCallFrame before the IL stub. Debug.Assert(handle.FrameIter.IsInlineCallFrameWithActiveCall()); @@ -193,10 +193,34 @@ private bool CheckForSkippedFrames(StackWalkData handle) IPlatformAgnosticContext parentContext = handle.Context.Clone(); parentContext.Unwind(_target); + if (_arch == RuntimeInfoArchitecture.X86) + { + return CheckForSkippedFramesX86(handle, parentContext); + } + + return handle.FrameIter.CurrentFrameAddress.Value < parentContext.StackPointer.Value; + } + + private bool CheckForSkippedFramesX86(StackWalkData handle, IPlatformAgnosticContext parentContext) + { + IExecutionManager eman = _target.Contracts.ExecutionManager; + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + while (handle.FrameIter.IsValid() && handle.FrameIter.CurrentFrameAddress.Value < parentContext.StackPointer.Value) { - if (_arch == RuntimeInfoArchitecture.X86 && handle.FrameIter.IsInlineCallFrameWithActiveCall()) + bool reportInteropMD = false; + if (eman.GetCodeBlockHandle(handle.Context.InstructionPointer.Value) is CodeBlockHandle cbh) + { + MethodDescHandle mdHandle = rts.GetMethodDescHandle(eman.GetMethodDesc(cbh)); + reportInteropMD = + handle.FrameIter.IsValid() && + handle.FrameIter.GetCurrentFrameType() == FrameIterator.FrameType.InlinedCallFrame && + rts.IsILStub(mdHandle) && + rts.HasMDContextArg(mdHandle); + } + + if (handle.FrameIter.IsInlineCallFrameWithActiveCall() && !reportInteropMD) { // On x86 we have already reported the InlinedCallFrame, don't report it again. handle.FrameIter.Next(); From 570659dcaeed82b67ce62dae74502bc6cad3274c Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Jun 2025 17:26:03 -0400 Subject: [PATCH 22/56] fix UnwindEspFrame --- .../Contracts/StackWalk/Context/X86/X86Unwinder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs index eb05e4bc136485..f6463f1522e1c4 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs @@ -313,8 +313,8 @@ private void UnwindEspFrame(ref X86Context context, GCInfo gcInfo, TargetCodePoi if (!gcInfo.SavedRegsMask.HasFlag(regMask)) continue; - // TODO(cdacx86): UpdateAllRegs set location?? - // SetLocation(pContext, i - 1, PTR_DWORD((TADDR)ESP)); + TargetPointer regValueFromStack = _target.ReadPointer(esp); + SetRegValue(ref context, regMask, regValueFromStack); // Pop the callee-saved registers esp += (uint)_target.PointerSize; From 7e75287cfc728be71a347cc487add98084943268 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Jun 2025 17:26:17 -0400 Subject: [PATCH 23/56] R2R find GCInfo correctly --- ...ecutionManagerCore.ReadyToRunJitManager.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index 0c42a728266c9c..bda13efb16657c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -152,6 +152,25 @@ public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jitt if (!_runtimeFunctions.TryGetRuntimeFunctionIndexForAddress(r2rInfo.RuntimeFunctions, r2rInfo.NumRuntimeFunctions, relativeAddr, out index)) return; + bool featureEHFunclets = Target.ReadGlobal(Constants.Globals.FeatureEHFunclets) != 0; + if (featureEHFunclets) + { + // Look up index in hot/cold map - if the function is in the cold part, get the index of the hot part. + index = _hotCold.GetHotFunctionIndex(r2rInfo.NumHotColdMap, r2rInfo.HotColdMap, index); + Debug.Assert(index < r2rInfo.NumRuntimeFunctions); + } + + TargetPointer methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index); + while (featureEHFunclets && methodDesc == TargetPointer.Null) + { + // Funclets won't have a direct entry in the map of runtime function entry point to method desc. + // The funclet's address (and index) will be greater than that of the corresponding function, so + // we decrement the index to find the actual function / method desc for the funclet. + index--; + methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index); + } + Debug.Assert(methodDesc != TargetPointer.Null); + Data.RuntimeFunction runtimeFunction = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, index); TargetPointer unwindInfo = runtimeFunction.UnwindData + imageBase; From d7a99f01c109e697b0a0c39c5824fe3efc3b74ba Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Jun 2025 17:27:11 -0400 Subject: [PATCH 24/56] Handle parsing ArgTab correctly when ArgTabOffset is not present --- .../Context/X86/GCInfoDecoding/GCInfo.cs | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs index ead11f8e8d5e27..918400667e85cf 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs @@ -129,8 +129,43 @@ public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) // Lazily initialize transitions. These are not present in all Heap dumps, only if they are required for stack walking. _transitions = new(() => { - // calculate the Argument Table pointer - TargetPointer argTabPtr = gcInfoAddress.Value + infoHdrSize + Header.ArgTabOffset; + TargetPointer argTabPtr; + if (Header.HasArgTabOffset) + { + // The GCInfo has an explicit argument table offset + argTabPtr = gcInfoAddress.Value + infoHdrSize + Header.ArgTabOffset; + } + else + { + // The GCInfo does not have an explicit argument table offset, we need to calculate it + // from the end of the header. The argument table is located after + // the NoGCRegions table, the UntrackedVariable table, and the FrameVariableLifetime table. + argTabPtr = gcInfoAddress.Value + infoHdrSize; + + /* Skip over the no GC regions table */ + for (int i = 0; i < Header.NoGCRegionCount; i++) + { + // The NoGCRegion table has a variable size, each entry is 2 unsigned integers. + target.GCDecodeUnsigned(ref argTabPtr); + target.GCDecodeUnsigned(ref argTabPtr); + } + + /* Skip over the untracked frame variable table */ + for (int i = 0; i < Header.UntrackedCount; i++) + { + // The UntrackedVariable table has a variable size, each entry is 1 signed integer. + target.GCDecodeSigned(ref argTabPtr); + } + + /* Skip over the frame variable lifetime table */ + for (int i = 0; i < Header.VarPtrTableSize; i++) + { + // The FrameVariableLifetime table has a variable size, each entry is 3 unsigned integer. + target.GCDecodeUnsigned(ref argTabPtr); + target.GCDecodeUnsigned(ref argTabPtr); + target.GCDecodeUnsigned(ref argTabPtr); + } + } GCArgTable argTable = new(_target, Header, argTabPtr); return argTable.Transitions; }); From d6ecb88f9f542d85839f68146bcdc0e5ee8f9670 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Jun 2025 17:30:52 -0400 Subject: [PATCH 25/56] loosen GetUsefulGlobals assertion --- .../mscordaccore_universal/Legacy/SOSDacImpl.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index a6360422ce5837..7c36d2cd100b11 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -1634,6 +1634,15 @@ int ISOSDacInterface.GetUsefulGlobals(DacpUsefulGlobalsData* data) catch (System.Exception ex) { hr = ex.HResult; + + // There are some scenarios where SOS can call GetUsefulGlobals before the globals are initialized, + // in these cases set the method table pointers to 0 and assert that the legacy DAC returns the same + // uninitialized values. + data->ArrayMethodTable = 0; + data->StringMethodTable = 0; + data->ObjectMethodTable = 0; + data->ExceptionMethodTable = 0; + data->FreeMethodTable = 0; } #if DEBUG @@ -1641,8 +1650,8 @@ int ISOSDacInterface.GetUsefulGlobals(DacpUsefulGlobalsData* data) { DacpUsefulGlobalsData dataLocal; int hrLocal = _legacyImpl.GetUsefulGlobals(&dataLocal); - Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); - if (hr == HResults.S_OK) + // Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK || hrLocal == HResults.S_OK) { Debug.Assert(data->ArrayMethodTable == dataLocal.ArrayMethodTable); Debug.Assert(data->StringMethodTable == dataLocal.StringMethodTable); From e1feb951d0abd5e77f347e80e1b716e0c5df6be4 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Jun 2025 19:13:56 -0400 Subject: [PATCH 26/56] remove prints --- .../StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs | 7 ------- .../cdac/mscordaccore_universal/Legacy/ClrDataModule.cs | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs index 918400667e85cf..8a6c039d35df5a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs @@ -176,19 +176,13 @@ public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) private uint CalculateDepth() { - using StreamWriter outputFile = new StreamWriter("C:\\Users\\maxcharlamb\\OneDrive - Microsoft\\Desktop\\out.txt", true); - int depth = 0; - outputFile.WriteLine($"depth: {depth}"); foreach (int offset in Transitions.Keys.OrderBy(i => i)) { if (offset > RelativeOffset) break; // calculate only to current offset - outputFile.WriteLine($"CodeOffset: {offset:x8}"); foreach (BaseGcTransition gcTransition in Transitions[offset]) { - outputFile.WriteLine(gcTransition); - switch (gcTransition) { case GcTransitionRegister gcTransitionRegister: @@ -223,7 +217,6 @@ private uint CalculateDepth() throw new InvalidOperationException("Unsupported gc transition type"); } } - outputFile.WriteLine($"depth: {depth}"); } return (uint)(depth * _target.PointerSize); diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs index 06a28ad8fa39a5..567092bea63c3c 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs @@ -149,7 +149,7 @@ int IXCLRDataModule.GetFileName(uint bufLen, uint* nameLen, char* name) if (string.IsNullOrEmpty(result)) return HResults.E_FAIL; - OutputBufferHelpers.CopyStringToBuffer(name, bufLen, nameLen, result); + OutputHelpers.CopyStringToBuffer(name, bufLen, nameLen, result); } catch (System.Exception ex) { From aeb8dc4166d241399a72cff1c8be274d47624c70 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Jun 2025 19:56:20 -0400 Subject: [PATCH 27/56] refactor ReadyToRunJitManager to de-duplicate RuntimeFunction lookup logic --- ...ecutionManagerCore.ReadyToRunJitManager.cs | 177 ++++++++---------- 1 file changed, 82 insertions(+), 95 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index bda13efb16657c..f5474e9800de39 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -30,50 +30,18 @@ public ReadyToRunJitManager(Target target) : base(target) public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) { // ReadyToRunJitManager::JitCodeToMethodInfo - if (rangeSection.Data == null) - throw new ArgumentException(nameof(rangeSection)); - info = default; - Debug.Assert(rangeSection.Data.R2RModule != TargetPointer.Null); - Data.Module r2rModule = Target.ProcessedData.GetOrAdd(rangeSection.Data.R2RModule); - Debug.Assert(r2rModule.ReadyToRunInfo != TargetPointer.Null); - Data.ReadyToRunInfo r2rInfo = Target.ProcessedData.GetOrAdd(r2rModule.ReadyToRunInfo); - - // Check if address is in a thunk - if (IsStubCodeBlockThunk(rangeSection.Data, r2rInfo, jittedCodeAddress)) + Data.ReadyToRunInfo r2rInfo = GetReadyToRunInfo(rangeSection); + if (!GetRuntimeFunction(rangeSection, r2rInfo, jittedCodeAddress, out TargetPointer imageBase, out uint index)) return false; - // Find the relative address that we are looking for - TargetPointer addr = CodePointerUtils.AddressFromCodePointer(jittedCodeAddress, Target); - TargetPointer imageBase = rangeSection.Data.RangeBegin; - TargetPointer relativeAddr = addr - imageBase; - - uint index; - if (!_runtimeFunctions.TryGetRuntimeFunctionIndexForAddress(r2rInfo.RuntimeFunctions, r2rInfo.NumRuntimeFunctions, relativeAddr, out index)) - return false; - - bool featureEHFunclets = Target.ReadGlobal(Constants.Globals.FeatureEHFunclets) != 0; - if (featureEHFunclets) - { - // Look up index in hot/cold map - if the function is in the cold part, get the index of the hot part. - index = _hotCold.GetHotFunctionIndex(r2rInfo.NumHotColdMap, r2rInfo.HotColdMap, index); - Debug.Assert(index < r2rInfo.NumRuntimeFunctions); - } - - TargetPointer methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index); - while (featureEHFunclets && methodDesc == TargetPointer.Null) - { - // Funclets won't have a direct entry in the map of runtime function entry point to method desc. - // The funclet's address (and index) will be greater than that of the corresponding function, so - // we decrement the index to find the actual function / method desc for the funclet. - index--; - methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index); - } + index = AdjustRuntimeFunctionIndexForHotCold(r2rInfo, index); + index = AdjustRuntimeFunctionToMethodStart(r2rInfo, imageBase, index, out TargetPointer methodDesc); - Debug.Assert(methodDesc != TargetPointer.Null); Data.RuntimeFunction function = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, index); + TargetPointer addr = CodePointerUtils.AddressFromCodePointer(jittedCodeAddress, Target); TargetCodePointer startAddress = imageBase + function.BeginAddress; TargetNUInt relativeOffset = new TargetNUInt(addr - startAddress); @@ -99,26 +67,8 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer public override TargetPointer GetUnwindInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress) { // ReadyToRunJitManager::JitCodeToMethodInfo - if (rangeSection.Data == null) - throw new ArgumentException(nameof(rangeSection)); - - Debug.Assert(rangeSection.Data.R2RModule != TargetPointer.Null); - - Data.Module r2rModule = Target.ProcessedData.GetOrAdd(rangeSection.Data.R2RModule); - Debug.Assert(r2rModule.ReadyToRunInfo != TargetPointer.Null); - Data.ReadyToRunInfo r2rInfo = Target.ProcessedData.GetOrAdd(r2rModule.ReadyToRunInfo); - - // Check if address is in a thunk - if (IsStubCodeBlockThunk(rangeSection.Data, r2rInfo, jittedCodeAddress)) - return TargetPointer.Null; - - // Find the relative address that we are looking for - TargetPointer addr = CodePointerUtils.AddressFromCodePointer(jittedCodeAddress, Target); - TargetPointer imageBase = rangeSection.Data.RangeBegin; - TargetPointer relativeAddr = addr - imageBase; - - uint index; - if (!_runtimeFunctions.TryGetRuntimeFunctionIndexForAddress(r2rInfo.RuntimeFunctions, r2rInfo.NumRuntimeFunctions, relativeAddr, out index)) + Data.ReadyToRunInfo r2rInfo = GetReadyToRunInfo(rangeSection); + if (!GetRuntimeFunction(rangeSection, r2rInfo, jittedCodeAddress, out TargetPointer _, out uint index)) return TargetPointer.Null; return _runtimeFunctions.GetRuntimeFunctionAddress(r2rInfo.RuntimeFunctions, index); @@ -130,46 +80,12 @@ public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jitt gcVersion = 0; // ReadyToRunJitManager::GetGCInfoToken - if (rangeSection.Data == null) - throw new ArgumentException(nameof(rangeSection)); - - Debug.Assert(rangeSection.Data.R2RModule != TargetPointer.Null); - - Data.Module r2rModule = Target.ProcessedData.GetOrAdd(rangeSection.Data.R2RModule); - Debug.Assert(r2rModule.ReadyToRunInfo != TargetPointer.Null); - Data.ReadyToRunInfo r2rInfo = Target.ProcessedData.GetOrAdd(r2rModule.ReadyToRunInfo); - - // Check if address is in a thunk - if (IsStubCodeBlockThunk(rangeSection.Data, r2rInfo, jittedCodeAddress)) + Data.ReadyToRunInfo r2rInfo = GetReadyToRunInfo(rangeSection); + if (!GetRuntimeFunction(rangeSection, r2rInfo, jittedCodeAddress, out TargetPointer imageBase, out uint index)) return; - // Find the relative address that we are looking for - TargetPointer addr = CodePointerUtils.AddressFromCodePointer(jittedCodeAddress, Target); - TargetPointer imageBase = rangeSection.Data.RangeBegin; - TargetPointer relativeAddr = addr - imageBase; - - uint index; - if (!_runtimeFunctions.TryGetRuntimeFunctionIndexForAddress(r2rInfo.RuntimeFunctions, r2rInfo.NumRuntimeFunctions, relativeAddr, out index)) - return; - - bool featureEHFunclets = Target.ReadGlobal(Constants.Globals.FeatureEHFunclets) != 0; - if (featureEHFunclets) - { - // Look up index in hot/cold map - if the function is in the cold part, get the index of the hot part. - index = _hotCold.GetHotFunctionIndex(r2rInfo.NumHotColdMap, r2rInfo.HotColdMap, index); - Debug.Assert(index < r2rInfo.NumRuntimeFunctions); - } - - TargetPointer methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index); - while (featureEHFunclets && methodDesc == TargetPointer.Null) - { - // Funclets won't have a direct entry in the map of runtime function entry point to method desc. - // The funclet's address (and index) will be greater than that of the corresponding function, so - // we decrement the index to find the actual function / method desc for the funclet. - index--; - methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index); - } - Debug.Assert(methodDesc != TargetPointer.Null); + index = AdjustRuntimeFunctionIndexForHotCold(r2rInfo, index); + index = AdjustRuntimeFunctionToMethodStart(r2rInfo, imageBase, index, out _); Data.RuntimeFunction runtimeFunction = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, index); @@ -204,5 +120,76 @@ private TargetPointer GetMethodDescForRuntimeFunction(Data.ReadyToRunInfo r2rInf return methodDesc; } + + #region Helpers + + private Data.ReadyToRunInfo GetReadyToRunInfo(RangeSection rangeSection) + { + if (rangeSection.Data == null) + throw new ArgumentException(nameof(rangeSection)); + + Debug.Assert(rangeSection.Data.R2RModule != TargetPointer.Null); + + Data.Module r2rModule = Target.ProcessedData.GetOrAdd(rangeSection.Data.R2RModule); + Debug.Assert(r2rModule.ReadyToRunInfo != TargetPointer.Null); + return Target.ProcessedData.GetOrAdd(r2rModule.ReadyToRunInfo); + } + + private bool GetRuntimeFunction( + RangeSection rangeSection, + Data.ReadyToRunInfo r2rInfo, + TargetCodePointer jittedCodeAddress, + out TargetPointer imageBase, + out uint runtimeFunctionIndex) + { + imageBase = TargetPointer.Null; + runtimeFunctionIndex = 0; + + if (rangeSection.Data == null) + throw new ArgumentException(nameof(rangeSection)); + + // Check if address is in a thunk + if (IsStubCodeBlockThunk(rangeSection.Data, r2rInfo, jittedCodeAddress)) + return false; + + // Find the relative address that we are looking for + TargetPointer addr = CodePointerUtils.AddressFromCodePointer(jittedCodeAddress, Target); + imageBase = rangeSection.Data.RangeBegin; + TargetPointer relativeAddr = addr - imageBase; + + return _runtimeFunctions.TryGetRuntimeFunctionIndexForAddress(r2rInfo.RuntimeFunctions, r2rInfo.NumRuntimeFunctions, relativeAddr, out runtimeFunctionIndex); + } + + private uint AdjustRuntimeFunctionIndexForHotCold(Data.ReadyToRunInfo r2rInfo, uint index) + { + bool featureEHFunclets = Target.ReadGlobal(Constants.Globals.FeatureEHFunclets) != 0; + if (featureEHFunclets) + { + // Look up index in hot/cold map - if the function is in the cold part, get the index of the hot part. + index = _hotCold.GetHotFunctionIndex(r2rInfo.NumHotColdMap, r2rInfo.HotColdMap, index); + Debug.Assert(index < r2rInfo.NumRuntimeFunctions); + } + return index; + } + + private uint AdjustRuntimeFunctionToMethodStart(Data.ReadyToRunInfo r2rInfo, TargetPointer imageBase, uint index, out TargetPointer methodDesc) + { + bool featureEHFunclets = Target.ReadGlobal(Constants.Globals.FeatureEHFunclets) != 0; + + methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index); + while (featureEHFunclets && methodDesc == TargetPointer.Null) + { + // Funclets won't have a direct entry in the map of runtime function entry point to method desc. + // The funclet's address (and index) will be greater than that of the corresponding function, so + // we decrement the index to find the actual function / method desc for the funclet. + index--; + methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index); + } + + Debug.Assert(methodDesc != TargetPointer.Null); + return index; + } + + #endregion } } From 61fbed54f1e39cefa1315a5f3997089fafc57572 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Jun 2025 10:24:26 -0400 Subject: [PATCH 28/56] comments/cleanup InfoHdr.cs --- .../Context/X86/GCInfoDecoding/InfoHdr.cs | 109 ++++++++++-------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs index d9e61fa6361e98..414d748e5c456f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; @@ -11,66 +12,68 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; /// /// based on src\inc\gcinfotypes.h InfoHdrSmall /// -public record struct InfoHdr +public struct InfoHdr { - public byte PrologSize { get; set; } - public byte EpilogSize { get; set; } - - public byte EpilogCount { get; set; } // 0 - 7 - public bool EpilogAtEnd { get; set; } - public bool EdiSaved { get; set; } - public bool EsiSaved { get; set; } - public bool EbxSaved { get; set; } - public bool EbpSaved { get; set; } - - public bool EbpFrame { get; set; } - public bool Interruptible { get; set; } - public bool DoubleAlign { get; set; } - public bool Security { get; set; } - public bool Handlers { get; set; } - public bool LocalAlloc { get; set; } - public bool EditAndContinue { get; set; } - public bool VarArgs { get; set; } - - public bool ProfCallbacks { get; set; } - public bool GenericsContext { get; set; } - public bool GenericsContextIsMethodDesc { get; set; } - public ReturnKinds ReturnKind { get; set; } - - public ushort ArgCount { get; set; } - public uint FrameSize { get; set; } - public uint UntrackedCount { get; set; } - public uint VarPtrTableSize { get; set; } - - // Fields not set by the initial table encoding - public uint GsCookieOffset { get; set; } = 0; - public uint SyncStartOffset { get; set; } = 0; - public uint SyndEndOffset { get; set; } = 0; - public uint RevPInvokeOffset { get; set; } = INVALID_REV_PINVOKE_OFFSET; - public uint NoGCRegionCount { get; set; } = 0; - - public bool HasArgTabOffset { get; set; } - public uint ArgTabOffset { get; set; } - public List Epilogs { get; set; } + /* Fields set by the initial table encoding */ + public byte PrologSize { get; private set; } + public byte EpilogSize { get; private set; } + + public byte EpilogCount { get; private set; } + public bool EpilogAtEnd { get; private set; } + public bool EdiSaved { get; private set; } + public bool EsiSaved { get; private set; } + public bool EbxSaved { get; private set; } + public bool EbpSaved { get; private set; } + + public bool EbpFrame { get; private set; } + public bool Interruptible { get; private set; } + public bool DoubleAlign { get; private set; } + public bool Security { get; private set; } + public bool Handlers { get; private set; } + public bool LocalAlloc { get; private set; } + public bool EditAndContinue { get; private set; } + public bool VarArgs { get; private set; } + + public bool ProfCallbacks { get; private set; } + public bool GenericsContext { get; private set; } + public bool GenericsContextIsMethodDesc { get; private set; } + public ReturnKinds ReturnKind { get; private set; } + + public ushort ArgCount { get; private set; } + public uint FrameSize { get; private set; } + public uint UntrackedCount { get; private set; } + public uint VarPtrTableSize { get; private set; } + + /* Fields not set by the initial table encoding */ + public uint GsCookieOffset { get; private set; } = 0; + public uint SyncStartOffset { get; private set; } = 0; + public uint SyndEndOffset { get; private set; } = 0; + public uint RevPInvokeOffset { get; private set; } = INVALID_REV_PINVOKE_OFFSET; + public uint NoGCRegionCount { get; private set; } = 0; + + public bool HasArgTabOffset { get; private set; } + public uint ArgTabOffset { get; private set; } + public ImmutableArray Epilogs { get; private set; } #region Adjustments private const byte SET_FRAMESIZE_MAX = 7; - private const byte SET_ARGCOUNT_MAX = 8; // Change to 6 + private const byte SET_ARGCOUNT_MAX = 8; private const byte SET_PROLOGSIZE_MAX = 16; - private const byte SET_EPILOGSIZE_MAX = 10; // Change to 6 + private const byte SET_EPILOGSIZE_MAX = 10; private const byte SET_EPILOGCNT_MAX = 4; private const byte SET_UNTRACKED_MAX = 3; - private const byte SET_RET_KIND_MAX = 3; // 2 bits for ReturnKind + private const byte SET_RET_KIND_MAX = 3; // 2 bits for ReturnKind private const byte SET_NOGCREGIONS_MAX = 4; - private const byte ADJ_ENCODING_MAX = 0x7f; // Maximum valid encoding in a byte - // Also used to mask off next bit from each encoding byte. + private const byte ADJ_ENCODING_MAX = 0x7f; // Maximum valid encoding in a byte + // Also used to mask off next bit from each encoding byte. private const byte MORE_BYTES_TO_FOLLOW = 0x80; // If the High-bit of a header or adjustment byte // is set, then there are more adjustments to follow. - // + /// // Enum to define codes that are used to incrementally adjust the InfoHdr structure. // First set of opcodes + /// private enum InfoHdrAdjust : byte { @@ -111,7 +114,10 @@ private enum InfoHdrAdjust : byte NEXT_THREE_EPILOGSIZE = 0x78 }; + /// + // Enum to define codes that are used to incrementally adjust the InfoHdr structure. // Second set of opcodes, when first code is 0x4F + /// private enum InfoHdrAdjust2 : uint { SET_RETURNKIND = 0, // 0x00-SET_RET_KIND_MAX Set ReturnKind to value @@ -124,7 +130,7 @@ public enum ReturnKinds RT_Scalar = 0, RT_Object = 1, RT_ByRef = 2, - RT_Unset = 3, // Encoding 3 means RT_Float on X86 + RT_Unset = 3, // Encoding 3 means RT_Float on X86 RT_Scalar_Obj = RT_Object << 2 | RT_Scalar, RT_Scalar_ByRef = RT_ByRef << 2 | RT_Scalar, @@ -144,7 +150,7 @@ public static InfoHdr DecodeHeader(Target target, ref TargetPointer offset, uint if (encoding < 0 || encoding >= INFO_HDR_TABLE.Length) { - throw new InvalidOperationException("Table is invalid."); + throw new InvalidOperationException("Table encoding is invalid."); } InfoHdr infoHdr = INFO_HDR_TABLE[encoding]; @@ -343,7 +349,7 @@ public static InfoHdr DecodeHeader(Target target, ref TargetPointer offset, uint infoHdr.HasArgTabOffset = true; } - infoHdr.Epilogs = new List(); + ImmutableArray.Builder epilogsBuilder = ImmutableArray.CreateBuilder(); if (infoHdr.EpilogCount > 1 || (infoHdr.EpilogCount != 0 && !infoHdr.EpilogAtEnd)) { uint offs = 0; @@ -351,14 +357,15 @@ public static InfoHdr DecodeHeader(Target target, ref TargetPointer offset, uint for (int i = 0; i < infoHdr.EpilogCount; i++) { offs = target.GCDecodeUDelta(ref offset, offs); - infoHdr.Epilogs.Add((int)offs); + epilogsBuilder.Add((int)offs); } } else { if (infoHdr.EpilogCount != 0) - infoHdr.Epilogs.Add((int)(codeLength - infoHdr.EpilogSize)); + epilogsBuilder.Add((int)(codeLength - infoHdr.EpilogSize)); } + infoHdr.Epilogs = epilogsBuilder.ToImmutable(); if (infoHdr.HasArgTabOffset) { From af6a9b16db308e3c278e8660baf6a2f6f70e9a7c Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Jun 2025 10:56:59 -0400 Subject: [PATCH 29/56] add rts docs --- .../design/datacontracts/RuntimeTypeSystem.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index b1d65b7c825273..a8be67be263081 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -138,6 +138,10 @@ partial interface IRuntimeTypeSystem : IContract // A IL Stub method is also a StoredSigMethodDesc, and a NoMetadataMethod public virtual bool IsILStub(MethodDescHandle methodDesc); + // Return true if the MethodDesc represents an IL Stub that takes a context argument + // that is an interop MethodDesc. + public virtual bool HasMDContextArg(MethodDescHandle methodDesc); + // Return true if a MethodDesc is in a collectible module public virtual bool IsCollectibleMethod(MethodDescHandle methodDesc); @@ -711,8 +715,14 @@ And the following enumeration definitions [Flags] internal enum DynamicMethodDescExtendedFlags : uint { + Static = 0x00001000, IsLCGMethod = 0x00004000, IsILStub = 0x00008000, + IsDelegate = 0x00010000, + IsCALLI = 0x00020000, + FlagMask = 0x0003f800, + StackArgSizeMask = 0xfffc0000, + ILStubTypeMask = ~(FlagMask | StackArgSizeMask), } [Flags] @@ -963,6 +973,30 @@ And the various apis are implemented with the following algorithms return ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsILStub); } + public bool HasMDContextArg(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodDescClassification.Dynamic) + { + return false; + } + + uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address + + uint ilStubType = (uint)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask); + + bool isStatic = ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.Static); + bool isDelegate = ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsDelegate); + bool isCALLI = ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsCALLI); + + + bool isPInvokeStub = isStatic && !isCALLI && ilStubType == 0x1; + bool isCLRToCOMStub = !isStatic && ilStubType == 0x2; + + return isCLRToCOMStub || (isPInvokeStub && !isDelegate); + } + public ushort GetSlotNumber(MethodDescHandle methodDesc) => _methodDescs[methodDesc.Addres]._desc.Slot; ``` From 82eb5aa5dfacc4c6fbc76d9cbeff6f1e8d1a6c2c Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Jun 2025 10:58:32 -0400 Subject: [PATCH 30/56] remove r2r GCDecoder change --- .../tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs index d3d13a21a82ec8..7852ebf962db60 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/InfoHdr.cs @@ -163,7 +163,7 @@ public class InfoHdrDecoder { private const uint HAS_UNTRACKED = 0xFFFFFFFF; private const uint HAS_GS_COOKIE_OFFSET = 0xFFFFFFFF; private const uint HAS_SYNC_OFFSET = 0xFFFFFFFF; - private const uint HAS_REV_PINVOKE_FRAME_OFFSET = 0xFFFFFFFF; // is this wrong?? + private const uint HAS_REV_PINVOKE_FRAME_OFFSET = 0xFFFFFFFF; private const uint HAS_NOGCREGIONS = 0xFFFFFFFF; private const uint YES = HAS_VARPTR; From ebf14c304cb0fe6deceea5203f2879928f6e4e64 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Jun 2025 11:16:28 -0400 Subject: [PATCH 31/56] clean up GCInfo.cs --- .../Context/X86/GCInfoDecoding/GCInfo.cs | 194 ++++++------------ 1 file changed, 61 insertions(+), 133 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs index 8a6c039d35df5a..8bfe0e01eedcec 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Linq; @@ -31,6 +32,9 @@ public record GCInfo { private readonly Target _target; + private readonly TargetPointer _gcInfoAddress; + private readonly uint _infoHdrSize; + public uint RelativeOffset { get; set; } public uint MethodSize { get; set; } public InfoHdr Header { get; set; } @@ -53,17 +57,23 @@ public record GCInfo public uint SavedRegsCountExclFP { get; set; } public RegMask SavedRegsMask { get; set; } = RegMask.NONE; - private Lazy>> _transitions = new(); - public Dictionary> Transitions => _transitions.Value; + /// + /// GC Transitions indexed by their relative offset. + /// + public ImmutableDictionary> Transitions => _transitions.Value; + private readonly Lazy>> _transitions = new(); - // Number of bytes of stack space that has been pushed for arguments at the current RelativeOffset. - private Lazy _pushedArgSize; + /// + /// Number of bytes of stack space that has been pushed for arguments at the current RelativeOffset. + /// public uint PushedArgSize => _pushedArgSize.Value; + private readonly Lazy _pushedArgSize; public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) { _target = target; + _gcInfoAddress = gcInfoAddress; TargetPointer offset = gcInfoAddress; MethodSize = target.GCDecodeUnsigned(ref offset); RelativeOffset = relativeOffset; @@ -72,7 +82,7 @@ public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) Debug.Assert(relativeOffset <= MethodSize); Header = InfoHdr.DecodeHeader(target, ref offset, MethodSize); - uint infoHdrSize = (uint)(offset.Value - gcInfoAddress.Value); + _infoHdrSize = (uint)(offset.Value - gcInfoAddress.Value); // Check if we are in the prolog if (relativeOffset < Header.PrologSize) @@ -89,11 +99,11 @@ public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) } } - // calculate raw stack size + // Calculate raw stack size uint frameDwordCount = Header.FrameSize; RawStackSize = frameDwordCount * (uint)target.PointerSize; - // calculate callee saved regs + // Calculate callee saved regs uint savedRegsCount = 0; RegMask savedRegs = RegMask.NONE; @@ -126,55 +136,58 @@ public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) SavedRegsCountExclFP--; } - // Lazily initialize transitions. These are not present in all Heap dumps, only if they are required for stack walking. - _transitions = new(() => + // Lazily decode GC transitions. These values are not present in all Heap dumps. Only when they are required for stack walking. + // Therefore, we can only read them when they are used by the stack walker. + _transitions = new(DecodeTransitions); + + // Lazily calculate the pushed argument size. This forces the transitions to be decoded. + _pushedArgSize = new(CalculatePushedArgSize); + } + + private ImmutableDictionary> DecodeTransitions() + { + TargetPointer argTabPtr; + if (Header.HasArgTabOffset) { - TargetPointer argTabPtr; - if (Header.HasArgTabOffset) + // The GCInfo has an explicit argument table offset + argTabPtr = _gcInfoAddress + _infoHdrSize + Header.ArgTabOffset; + } + else + { + // The GCInfo does not have an explicit argument table offset, we need to calculate it + // from the end of the header. The argument table is located after + // the NoGCRegions table, the UntrackedVariable table, and the FrameVariableLifetime table. + argTabPtr = _gcInfoAddress + _infoHdrSize; + + /* Skip over the no GC regions table */ + for (int i = 0; i < Header.NoGCRegionCount; i++) { - // The GCInfo has an explicit argument table offset - argTabPtr = gcInfoAddress.Value + infoHdrSize + Header.ArgTabOffset; + // The NoGCRegion table has a variable size, each entry is 2 unsigned integers. + _target.GCDecodeUnsigned(ref argTabPtr); + _target.GCDecodeUnsigned(ref argTabPtr); } - else - { - // The GCInfo does not have an explicit argument table offset, we need to calculate it - // from the end of the header. The argument table is located after - // the NoGCRegions table, the UntrackedVariable table, and the FrameVariableLifetime table. - argTabPtr = gcInfoAddress.Value + infoHdrSize; - - /* Skip over the no GC regions table */ - for (int i = 0; i < Header.NoGCRegionCount; i++) - { - // The NoGCRegion table has a variable size, each entry is 2 unsigned integers. - target.GCDecodeUnsigned(ref argTabPtr); - target.GCDecodeUnsigned(ref argTabPtr); - } - - /* Skip over the untracked frame variable table */ - for (int i = 0; i < Header.UntrackedCount; i++) - { - // The UntrackedVariable table has a variable size, each entry is 1 signed integer. - target.GCDecodeSigned(ref argTabPtr); - } - /* Skip over the frame variable lifetime table */ - for (int i = 0; i < Header.VarPtrTableSize; i++) - { - // The FrameVariableLifetime table has a variable size, each entry is 3 unsigned integer. - target.GCDecodeUnsigned(ref argTabPtr); - target.GCDecodeUnsigned(ref argTabPtr); - target.GCDecodeUnsigned(ref argTabPtr); - } + /* Skip over the untracked frame variable table */ + for (int i = 0; i < Header.UntrackedCount; i++) + { + // The UntrackedVariable table has a variable size, each entry is 1 signed integer. + _target.GCDecodeSigned(ref argTabPtr); } - GCArgTable argTable = new(_target, Header, argTabPtr); - return argTable.Transitions; - }); - // Lazily initialize the pushed argument size. This relies on the transitions being calculated first. - _pushedArgSize = new(CalculateDepth); + /* Skip over the frame variable lifetime table */ + for (int i = 0; i < Header.VarPtrTableSize; i++) + { + // The FrameVariableLifetime table has a variable size, each entry is 3 unsigned integer. + _target.GCDecodeUnsigned(ref argTabPtr); + _target.GCDecodeUnsigned(ref argTabPtr); + _target.GCDecodeUnsigned(ref argTabPtr); + } + } + GCArgTable argTable = new(_target, Header, argTabPtr); + return argTable.Transitions.ToImmutableDictionary(); } - private uint CalculateDepth() + private uint CalculatePushedArgSize() { int depth = 0; foreach (int offset in Transitions.Keys.OrderBy(i => i)) @@ -222,88 +235,3 @@ private uint CalculateDepth() return (uint)(depth * _target.PointerSize); } } - -public class GCInfoDecoder(Target target) -{ - private readonly Target _target = target; - - // DecodeGCHDrInfo in src/coreclr/vm/gc_unwind_x86.inl - public TargetPointer DecodeGCHeaderInfo(TargetPointer gcInfoAddress, uint relativeOffset) - { - TargetPointer offset = gcInfoAddress; - uint methodSize = _target.GCDecodeUnsigned(ref offset); - - Debug.Assert(relativeOffset >= 0); - Debug.Assert(relativeOffset <= methodSize); - - return TargetPointer.Null; - - // InfoHdr infoHdr = InfoHdr.DecodeHeader(_target, ref offset, methodSize); - // uint infoHdrSize = (uint)(offset.Value - gcInfoAddress.Value); - - // gcInfo = new GCInfo - // { - // RelativeOffset = relativeOffset, - // MethodSize = methodSize, - // Header = infoHdr, - // }; - - // // Check if we are in the prolog - // if (relativeOffset < infoHdr.PrologSize) - // { - // gcInfo.PrologOffset = relativeOffset; - // } - - // // Check if we are in an epilog - // foreach (uint epilogStart in gcInfo.Header.Epilogs) - // { - // if (relativeOffset > epilogStart && relativeOffset < epilogStart + gcInfo.Header.EpilogSize) - // { - // gcInfo.EpilogOffset = relativeOffset - epilogStart; - // } - // } - - // // calculate raw stack size - // uint frameDwordCount = infoHdr.FrameSize; - // gcInfo.RawStackSize = frameDwordCount * (uint)_target.PointerSize; - - // // calculate callee saved regs - // uint savedRegsCount = 0; - // RegMask savedRegs = RegMask.NONE; - - // if (infoHdr.EdiSaved) - // { - // savedRegsCount++; - // savedRegs |= RegMask.EDI; - // } - // if (infoHdr.EsiSaved) - // { - // savedRegsCount++; - // savedRegs |= RegMask.ESI; - // } - // if (infoHdr.EbxSaved) - // { - // savedRegsCount++; - // savedRegs |= RegMask.EBX; - // } - // if (infoHdr.EbpSaved) - // { - // savedRegsCount++; - // savedRegs |= RegMask.EBP; - // } - - // gcInfo.SavedRegsCountExclFP = savedRegsCount; - // gcInfo.SavedRegsMask = savedRegs; - // if (infoHdr.EbpFrame || infoHdr.DoubleAlign) - // { - // Debug.Assert(infoHdr.EbpSaved); - // gcInfo.SavedRegsCountExclFP--; - // } - - // gcInfo.NoGCRegions = new NoGcRegionTable(_target, infoHdr, ref offset); - - // gcInfo.SlotTable = new GcSlotTable(_target, infoHdr, ref offset); - - // return TargetPointer.Null; - } -} From 350b993d61f531e982bc25fcd3d45ea4f6593e52 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Jun 2025 11:42:05 -0400 Subject: [PATCH 32/56] move x86 specific stackwalking helpers into namespace --- .../Context/X86/GCInfoDecoding/CallPattern.cs | 12 ++++---- .../Context/X86/GCInfoDecoding/GCArgTable.cs | 2 +- .../Context/X86/GCInfoDecoding/GCInfo.cs | 2 +- .../GCInfoDecoding/GCInfoTargetExtensions.cs | 10 +++++-- .../X86/GCInfoDecoding/GCTransition.cs | 2 +- .../Context/X86/GCInfoDecoding/InfoHdr.cs | 3 +- .../StackWalk/Context/X86/X86Unwinder.cs | 2 +- .../Contracts/StackWalk/Context/X86Context.cs | 1 + .../Contracts/StackWalk/StackWalk_1.cs | 29 +++++++++---------- 9 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/CallPattern.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/CallPattern.cs index c9c39ecb3611ff..7c3d7df15a5e3b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/CallPattern.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/CallPattern.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; -using System.Text; -namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; internal static class CallPattern { @@ -26,13 +24,13 @@ public static void DecodeCallPattern(uint pattern, out uint argCnt, out uint reg /// /// based on src\inc\gcdecoder.cpp callCommonDelta /// - public static uint[] callCommonDelta = { 6, 8, 10, 12 }; + public static readonly uint[] callCommonDelta = [6, 8, 10, 12]; /// /// based on src\inc\gcdecoder.cpp callPatternTable /// - private static uint[] callPatternTable = - { + private static readonly uint[] callPatternTable = + [ 0x0a000200, // 30109 0x0c000200, // 22970 0x0c000201, // 19005 @@ -113,5 +111,5 @@ public static void DecodeCallPattern(uint pattern, out uint argCnt, out uint reg 0x0c000601, // 1737 0x09000700, // 1737 0x07000300, // 1684 - }; + ]; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs index 27f51252226efc..2c3bc5ab52eaba 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; public class GCArgTable { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs index 8bfe0e01eedcec..9b6289e76c5352 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs @@ -8,7 +8,7 @@ using System.IO; using System.Linq; -namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; [Flags] public enum RegMask diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfoTargetExtensions.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfoTargetExtensions.cs index b7366f54242cc3..8634e0cde7afbe 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfoTargetExtensions.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfoTargetExtensions.cs @@ -3,10 +3,13 @@ using System; -namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; public static class GCInfoTargetExtensions { + /// + /// Based on src\inc\gcdecoder.cpp decodeUnsigned + /// public static uint GCDecodeUnsigned(this Target target, ref TargetPointer src) { TargetPointer begin = src; @@ -27,6 +30,9 @@ public static uint GCDecodeUnsigned(this Target target, ref TargetPointer src) return value; } + /// + /// Based on src\inc\gcdecoder.cpp decodeSigned + /// public static int GCDecodeSigned(this Target target, ref TargetPointer src) { TargetPointer begin = src; @@ -54,7 +60,7 @@ public static int GCDecodeSigned(this Target target, ref TargetPointer src) } /// - /// based on src\inc\gcdecoder.cpp decodeUDelta + /// Based on src\inc\gcdecoder.cpp decodeUDelta /// public static uint GCDecodeUDelta(this Target target, ref TargetPointer src, uint lastValue) { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCTransition.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCTransition.cs index f647c4bb301bff..71e9003e997e6a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCTransition.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCTransition.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Text; -namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; public enum Action { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs index 414d748e5c456f..e3a06018349725 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs @@ -3,11 +3,10 @@ using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; /// /// based on src\inc\gcinfotypes.h InfoHdrSmall diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs index f6463f1522e1c4..f322f49d841eb7 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs @@ -7,7 +7,7 @@ using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; using static Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86Context; -namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; public class X86Unwinder(Target target) { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs index 03f7f5600c3c62..37d3e92a4f19ee 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.InteropServices; +using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs index 6adef7b2f15763..5763c31c8d4887 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs @@ -2,11 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; using System.Diagnostics.CodeAnalysis; using System.Diagnostics; using System.Collections.Generic; using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; +using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; +using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -121,10 +122,8 @@ private void UpdateState(StackWalkData handle, IPlatformAgnosticContext prevCont bool validFrame = handle.FrameIter.IsValid(); // - // Platform specific checks + // Platform specific logic // - - // x86 Specific Checks if (_arch == RuntimeInfoArchitecture.X86) { if (IsManaged(prevContext.InstructionPointer, out CodeBlockHandle? prevCbh)) @@ -166,18 +165,11 @@ private void UpdateState(StackWalkData handle, IPlatformAgnosticContext prevCont } } - // - // If an explicit frame is allocated in a managed stack frame (e.g. an inlined pinvoke call), - // we may have skipped an explicit frame. This function checks for them. - // - // Return Value: - // Returns true if there are skipped frames. - // - // Notes: - // x86 wants to stop at the skipped stack frames after the containing managed stack frame, but - // WIN64 wants to stop before. I don't think x86 actually has any good reason for this, except - // because it doesn't unwind one frame ahead of time like WIN64 does. This means that we don't - // have the caller SP on x86. + /// + /// If an explicit frame is allocated in a managed stack frame (e.g. an inlined pinvoke call), + /// we may have skipped an explicit frame. This function checks for them. + /// + /// true if there are skipped frames. private bool CheckForSkippedFrames(StackWalkData handle) { // ensure we can find the caller context @@ -201,6 +193,11 @@ private bool CheckForSkippedFrames(StackWalkData handle) return handle.FrameIter.CurrentFrameAddress.Value < parentContext.StackPointer.Value; } + /// + /// X86 specific check for skipped frames. Different than other platforms due to X86 + /// only reporting the InlinedCallFrame once unless it is an IL stub with an MD context argument. + /// + /// true if there are skipped frames. private bool CheckForSkippedFramesX86(StackWalkData handle, IPlatformAgnosticContext parentContext) { IExecutionManager eman = _target.Contracts.ExecutionManager; From 233615e8526d83733e2b388d8771fd5991f9485e Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Jun 2025 13:48:45 -0400 Subject: [PATCH 33/56] import nit --- .../cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs index 403efe66ec564b..8440611106be13 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataStackWalk.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using Microsoft.Diagnostics.DataContractReader.Contracts; From dce3e020d9252051d93447563edf67b36b73df0e Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Jun 2025 14:34:32 -0400 Subject: [PATCH 34/56] fix --- .../managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs index 567092bea63c3c..06a28ad8fa39a5 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs @@ -149,7 +149,7 @@ int IXCLRDataModule.GetFileName(uint bufLen, uint* nameLen, char* name) if (string.IsNullOrEmpty(result)) return HResults.E_FAIL; - OutputHelpers.CopyStringToBuffer(name, bufLen, nameLen, result); + OutputBufferHelpers.CopyStringToBuffer(name, bufLen, nameLen, result); } catch (System.Exception ex) { From dff89fd8e83f655754d213c361a4b9ce062d6d59 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Jun 2025 15:28:42 -0400 Subject: [PATCH 35/56] overriding increment/decrement is not required --- .../TargetPointer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetPointer.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetPointer.cs index 845e89b08bce06..d145347f3220a9 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetPointer.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetPointer.cs @@ -16,8 +16,6 @@ namespace Microsoft.Diagnostics.DataContractReader; public static implicit operator ulong(TargetPointer p) => p.Value; public static implicit operator TargetPointer(ulong v) => new TargetPointer(v); - public static TargetPointer operator ++(TargetPointer p) => new TargetPointer(p.Value + 1); - public static TargetPointer operator --(TargetPointer p) => new TargetPointer(p.Value - 1); public static bool operator ==(TargetPointer left, TargetPointer right) => left.Value == right.Value; public static bool operator !=(TargetPointer left, TargetPointer right) => left.Value != right.Value; From 2170a9fa23f2ead8a74438823be3de9c79034bc4 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Jun 2025 15:29:11 -0400 Subject: [PATCH 36/56] fix some TODOs in the X86 Unwinder --- .../StackWalk/Context/X86/X86Unwinder.cs | 119 +++++++++--------- .../FrameHandling/X86FrameHandler.cs | 2 +- 2 files changed, 60 insertions(+), 61 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs index f322f49d841eb7..cd9d6e19da7d15 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs @@ -12,6 +12,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X8 public class X86Unwinder(Target target) { private readonly Target _target = target; + private readonly uint _pointerSize = (uint)target.PointerSize; private readonly bool _updateAllRegs = true; private readonly bool _unixX86ABI = target.Contracts.RuntimeInfo.GetTargetOperatingSystem() == RuntimeInfoOperatingSystem.Unix; @@ -37,8 +38,8 @@ public bool Unwind(ref X86Context context) eman.GetGCInfo(cbh, out TargetPointer gcInfoAddress, out uint _); uint relOffset = (uint)eman.GetRelativeOffset(cbh).Value; - TargetCodePointer methodStart = eman.GetStartAddress(cbh); - TargetCodePointer funcletStart = eman.GetFuncletStartAddress(cbh); + TargetPointer methodStart = eman.GetStartAddress(cbh).AsTargetPointer; + TargetPointer funcletStart = eman.GetFuncletStartAddress(cbh).AsTargetPointer; bool isFunclet = eman.IsFunclet(cbh); GCInfo gcInfo = new(_target, gcInfoAddress, relOffset); @@ -145,7 +146,7 @@ have already been popped */ // "pop ecx" will make ESP point to the callee-saved registers if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) { - esp += (uint)_target.PointerSize; + esp += _pointerSize; } offset = SKIP_POP_REG(epilogBase, offset); } @@ -161,7 +162,7 @@ have already been popped */ { // lea esp, [ebp-calleeSavedRegs] - uint calleeSavedRegsSize = gcInfo.SavedRegsCountExclFP * (uint)_target.PointerSize; + uint calleeSavedRegsSize = gcInfo.SavedRegsCountExclFP * _pointerSize; if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) { @@ -186,8 +187,12 @@ have already been popped */ if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) { - // TODO(cdacX86): UpdateAllRegs set location?? - esp += (uint)_target.PointerSize; + if (_updateAllRegs) + { + TargetPointer regValueFromStack = _target.ReadPointer(esp); + SetRegValue(ref context, regMask, regValueFromStack); + } + esp += _pointerSize; } offset = SKIP_POP_REG(epilogBase, offset); @@ -206,16 +211,12 @@ have already been popped */ { // TODO(cdacx86): are these equivalent? // pContext->SetEbpLocation(PTR_DWORD(TADDR(ESP))); - // context.Ebp = _target.Read(esp); - esp += (uint)_target.PointerSize; + context.Ebp = _target.Read(esp); + esp += _pointerSize; } _ = SKIP_POP_REG(epilogBase, offset); - - // TODO(cdacx86): are these equivalent? - // SetRegdisplayPCTAddr(pContext, (TADDR)ESP); context.Eip = _target.Read(esp); - context.Esp = esp; } @@ -263,8 +264,12 @@ regsMask as well to exclude registers which have already been popped. */ { /* We have NOT yet popped off the register. Get the value from the stack if needed */ - // TODO(cdacX86): UpdateAllRegs set location?? - esp += (uint)_target.PointerSize; + if (_updateAllRegs || regMask == RegMask.EBP) + { + TargetPointer regValueFromStack = _target.ReadPointer(esp); + SetRegValue(ref context, regMask, regValueFromStack); + } + esp += _pointerSize; } offset = SKIP_POP_REG(epilogBase, offset); @@ -277,14 +282,11 @@ regsMask as well to exclude registers which have already been popped. */ || CheckInstrWord(ReadShortAt(epilogBase + offset), X86_INSTR_w_JMP_FAR_IND_IMM)); //jmp [addr32] /* Finally we can set pPC */ - // TODO(cdacx86): are these equivalent? - // SetRegdisplayPCTAddr(pContext, (TADDR)ESP); context.Eip = _target.Read(esp); - context.Esp = esp; } - private void UnwindEspFrame(ref X86Context context, GCInfo gcInfo, TargetCodePointer methodStart) + private void UnwindEspFrame(ref X86Context context, GCInfo gcInfo, TargetPointer methodStart) { Debug.Assert(!gcInfo.Header.EbpFrame && !gcInfo.Header.DoubleAlign); Debug.Assert(!gcInfo.IsInEpilog); @@ -317,26 +319,30 @@ private void UnwindEspFrame(ref X86Context context, GCInfo gcInfo, TargetCodePoi SetRegValue(ref context, regMask, regValueFromStack); // Pop the callee-saved registers - esp += (uint)_target.PointerSize; + esp += _pointerSize; } } /* we can now set the (address of the) return address */ - // TODO(cdacx86): are these equivalent? - // SetRegdisplayPCTAddr(pContext, (TADDR)ESP); context.Eip = _target.Read(esp); - context.Esp = esp + ESPIncrementOnReturn(gcInfo); } - private void UnwindEspFrameProlog(ref X86Context context, GCInfo gcInfo, TargetCodePointer methodStart) + private void UnwindEspFrameProlog(ref X86Context context, GCInfo gcInfo, TargetPointer methodStart) { Debug.Assert(gcInfo.IsInProlog); Debug.Assert(!gcInfo.Header.EbpFrame && !gcInfo.Header.DoubleAlign); uint offset = 0; - // TODO(cdacX86): Why only check for methods with JitHalt in debug builds? + // TODO(cdacX86): JitHalt DEBUG only check. Can this always exist or should it really depend + // on the target runtimes configuration? + // If the first two instructions are 'nop, int3', then we will + // assume that is from a JitHalt operation and skip past it + if (ReadByteAt(methodStart) == X86_INSTR_NOP && ReadByteAt(methodStart + 1) == X86_INSTR_INT3) + { + offset += 2; + } uint curOffs = gcInfo.PrologOffset; uint esp = context.Esp; @@ -352,7 +358,7 @@ private void UnwindEspFrameProlog(ref X86Context context, GCInfo gcInfo, TargetC if (InstructionAlreadyExecuted(offset, curOffs)) { - esp += (uint)_target.PointerSize; + esp += _pointerSize; regsMask |= regMask; } @@ -385,7 +391,7 @@ private void UnwindEspFrameProlog(ref X86Context context, GCInfo gcInfo, TargetC if (regsMask.HasFlag(RegMask.EBP)) { context.Ebp = _target.Read(savedRegPtr); - savedRegPtr += (uint)_target.PointerSize; + savedRegPtr += _pointerSize; } if (_updateAllRegs) @@ -393,42 +399,31 @@ private void UnwindEspFrameProlog(ref X86Context context, GCInfo gcInfo, TargetC if (regsMask.HasFlag(RegMask.EBX)) { context.Ebx = _target.Read(savedRegPtr); - savedRegPtr += (uint)_target.PointerSize; + savedRegPtr += _pointerSize; } if (regsMask.HasFlag(RegMask.ESI)) { context.Esi = _target.Read(savedRegPtr); - savedRegPtr += (uint)_target.PointerSize; + savedRegPtr += _pointerSize; } if (regsMask.HasFlag(RegMask.EDI)) { context.Edi = _target.Read(savedRegPtr); - savedRegPtr += (uint)_target.PointerSize; + savedRegPtr += _pointerSize; } - // TODO(cdacX86): Do we handle trashing anything here? + // TODO(cdacX86): Should cDAC trash caller saved registers? // TRASH_CALLEE_UNSAVED_REGS(pContext); } - // NOTE: - // THIS IS ONLY TRUE IF PROLOGSIZE DOES NOT INCLUDE REG-VAR INITIALIZATION !!!! - // - /* there is (potentially) only one additional - instruction in the prolog, (push ebp) - but if we would have been passed that instruction, - info->prologOffs would be hdrInfo::NOT_IN_PROLOG! - */ - // TODO(cdacX86): Does this check matter? It was disabled in the runtime - //_ASSERTE(offset == info->prologOffs); - context.Esp = esp; } private bool UnwindEbpDoubleAlignFrame( ref X86Context context, GCInfo gcInfo, - TargetCodePointer methodStart, - TargetCodePointer funcletStart, + TargetPointer methodStart, + TargetPointer funcletStart, bool isFunclet) { Debug.Assert(gcInfo.Header.EbpFrame || gcInfo.Header.DoubleAlign); @@ -461,11 +456,12 @@ private bool UnwindEbpDoubleAlignFrame( } context.Eip = (uint)_target.ReadPointer(baseSP); - context.Esp = (uint)baseSP + (uint)_target.PointerSize; + context.Esp = (uint)baseSP + _pointerSize; return true; } - // TODO(cdacX86): Handle filter frames + /* The cDAC only supports FEATURE_EH_FUNCLETS and therefore does not + support unwinding filters without funclets. */ } // @@ -487,7 +483,7 @@ private bool UnwindEbpDoubleAlignFrame( // Get to the first callee-saved register TargetPointer pSavedRegs = curEbp; if (gcInfo.Header.DoubleAlign && (curEbp & 0x04) != 0) - pSavedRegs -= (uint)_target.PointerSize; + pSavedRegs -= _pointerSize; foreach (RegMask regMask in registerOrder.Reverse()) { @@ -495,17 +491,17 @@ private bool UnwindEbpDoubleAlignFrame( if (!gcInfo.SavedRegsMask.HasFlag(regMask)) continue; - pSavedRegs -= (uint)_target.PointerSize; + pSavedRegs -= _pointerSize; TargetPointer regValueFromStack = _target.ReadPointer(pSavedRegs); SetRegValue(ref context, regMask, regValueFromStack); } } /* The caller's ESP will be equal to EBP + retAddrSize + argSize. */ - context.Esp = curEbp + (uint)_target.PointerSize + ESPIncrementOnReturn(gcInfo); + context.Esp = curEbp + _pointerSize + ESPIncrementOnReturn(gcInfo); /* The caller's saved EIP is right after our EBP */ - context.Eip = (uint)_target.ReadPointer(curEbp + (uint)_target.PointerSize); + context.Eip = (uint)_target.ReadPointer(curEbp + _pointerSize); /* The caller's saved EBP is pointed to by our EBP */ context.Ebp = (uint)_target.ReadPointer(curEbp); @@ -519,7 +515,14 @@ private void UnwindEbpDoubleAlignFrameProlog(ref X86Context context, GCInfo gcIn uint offset = 0; - // TODO(cdacX86): Why only check for methods with JitHalt in debug builds? + // TODO(cdacX86): JitHalt DEBUG only check. Can this always exist or should it really depend + // on the target runtimes configuration? + // If the first two instructions are 'nop, int3', then we will + // assume that is from a JitHalt operation and skip past it + if (ReadByteAt(methodStart) == X86_INSTR_NOP && ReadByteAt(methodStart + 1) == X86_INSTR_INT3) + { + offset += 2; + } /* Check for the case where EBP has not been updated yet. */ uint curOffs = gcInfo.PrologOffset; @@ -535,7 +538,7 @@ private void UnwindEbpDoubleAlignFrameProlog(ref X86Context context, GCInfo gcIn /* If we're past the "push ebp", adjust ESP to pop EBP off */ if (curOffs == (offset + 1)) - context.Esp += (uint)_target.PointerSize; + context.Esp += _pointerSize; /* Stack pointer points to return address */ context.Eip = (uint)_target.ReadPointer(context.Esp); @@ -575,7 +578,7 @@ registers have been pushed already */ if (InstructionAlreadyExecuted(offset, curOffs)) { - pSavedRegs -= (uint)_target.PointerSize; + pSavedRegs -= _pointerSize; TargetPointer regValueFromStack = _target.ReadPointer(pSavedRegs); SetRegValue(ref context, regMask, regValueFromStack); } @@ -583,13 +586,13 @@ registers have been pushed already */ offset = SKIP_PUSH_REG(methodStart, offset); } - // TODO(cdacX86): Do we handle trashing anything here? + // TODO(cdacX86): Should cDAC trash caller saved registers? // TRASH_CALLEE_UNSAVED_REGS(pContext); } /* The caller's saved EBP is pointed to by our EBP */ context.Ebp = (uint)_target.ReadPointer(curEBP); - context.Esp = (uint)_target.ReadPointer(curEBP + (uint)_target.PointerSize); + context.Esp = (uint)_target.ReadPointer(curEBP + _pointerSize); /* Stack pointer points to return address */ context.Eip = (uint)_target.ReadPointer(context.Esp); @@ -614,8 +617,8 @@ private uint ESPIncrementOnReturn(GCInfo gcInfo) { uint stackParameterSize = gcInfo.Header.VarArgs ? 0 // varargs are caller-popped - : gcInfo.Header.ArgCount * (uint)_target.PointerSize; - return (uint)_target.PointerSize /* return address size */ + stackParameterSize; + : gcInfo.Header.ArgCount * _pointerSize; + return _pointerSize /* return address size */ + stackParameterSize; } // skips past a "arith REG, IMM" @@ -709,8 +712,6 @@ private uint SKIP_ALLOC_FRAME(int size, TargetPointer baseAddress, uint offset) if (CheckInstrWord(wOpcode, X86_INSTR_w_TEST_ESP_DWORD_OFFSET_EAX)) { - // TODO(cdacX86): This whole section is probably irrelevant for modern versions. - // In .NET 5.0 and earlier for frames that have size smaller than 0x3000 bytes // JIT emits one or two 'test eax, [esp-dwOffset]' instructions before adjusting the stack pointer. Debug.Assert(size < 0x3000); @@ -744,8 +745,6 @@ private uint SKIP_ALLOC_FRAME(int size, TargetPointer baseAddress, uint offset) if (CheckInstrByte(ReadByteAt(baseAddress + offset), X86_INSTR_XOR)) { - // TODO(cdacX86): This whole section is probably irrelevant for modern versions. - // In .NET Core 3.1 and earlier for frames that have size greater than or equal to 0x3000 bytes // JIT emits the following loop. Debug.Assert(size >= 0x3000); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs index 2ae2b69ea3492c..337dcff688f60e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs @@ -26,7 +26,7 @@ void IPlatformFrameHandler.HandleFaultingExceptionFrame(FaultingExceptionFrame f void IPlatformFrameHandler.HandleHijackFrame(HijackFrame frame) { - // TODO(cdacX86): + // TODO(cdacX86): Implement handling for HijackFrame throw new NotImplementedException(); } } From 9710bde4c0d7dacad6278796917b89327554634d Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Jun 2025 16:54:02 -0400 Subject: [PATCH 37/56] use xor for InfoHdr REV_PINVOKE_OFFSET --- .../Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs index e3a06018349725..fd60ed855ef190 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/InfoHdr.cs @@ -249,8 +249,7 @@ public static InfoHdr DecodeHeader(Target target, ref TargetPointer offset, uint infoHdr.SyncStartOffset ^= HAS_SYNC_OFFSET; break; case (byte)InfoHdrAdjust.FLIP_REV_PINVOKE_FRAME: - infoHdr.RevPInvokeOffset = infoHdr.RevPInvokeOffset == INVALID_REV_PINVOKE_OFFSET ? - HAS_REV_PINVOKE_FRAME_OFFSET : INVALID_REV_PINVOKE_OFFSET; + infoHdr.RevPInvokeOffset ^= INVALID_REV_PINVOKE_OFFSET ^ HAS_REV_PINVOKE_FRAME_OFFSET; break; case (byte)InfoHdrAdjust.NEXT_OPCODE: From 43eed4f3243d2c68615a54dfed25ff1710642a96 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 4 Jun 2025 14:08:28 -0400 Subject: [PATCH 38/56] convert to use ClrDataAddress --- .../Legacy/ClrDataAddress.cs | 36 ++ .../Legacy/ClrDataModule.cs | 8 +- .../Legacy/ConversionExtensions.cs | 64 +++ .../Legacy/ISOSDacInterface.cs | 312 +++++++------- .../Legacy/IXCLRData.cs | 34 +- .../Legacy/SOSDacImpl.IXCLRDataProcess.cs | 26 +- .../Legacy/SOSDacImpl.cs | 380 +++++++++--------- 7 files changed, 486 insertions(+), 374 deletions(-) create mode 100644 src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataAddress.cs create mode 100644 src/native/managed/cdac/mscordaccore_universal/Legacy/ConversionExtensions.cs diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataAddress.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataAddress.cs new file mode 100644 index 00000000000000..dd589378a75cc4 --- /dev/null +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataAddress.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices.Marshalling; + +namespace Microsoft.Diagnostics.DataContractReader.Legacy; + +/// +/// Represents the native CLRDATA_ADDRESS 64-bit type which uses sign extending +/// when converting from 32-bit values to 64-bit values. +/// For more information see TO_CDADDR in dacimpl.h +/// +[NativeMarshalling(typeof(ClrDataAddressMarshaller))] +internal struct ClrDataAddress : IEquatable +{ + public ulong Value; + + public ClrDataAddress(ulong value) => Value = value; + + public static implicit operator ulong(ClrDataAddress a) => a.Value; + public static implicit operator ClrDataAddress(ulong v) => new ClrDataAddress(v); + + public override bool Equals(object? obj) => obj is ClrDataAddress address && Equals(address); + public readonly bool Equals(ClrDataAddress other) => Value == other.Value; + public override readonly int GetHashCode() => Value.GetHashCode(); + + public override readonly string ToString() => $"0x{Value:x}"; +} + +[CustomMarshaller(typeof(ClrDataAddress), MarshalMode.Default, typeof(ClrDataAddressMarshaller))] +internal static class ClrDataAddressMarshaller +{ + public static ClrDataAddress ConvertToManaged(ulong address) => new ClrDataAddress(address); + public static ulong ConvertToUnmanaged(ClrDataAddress address) => address.Value; +} diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs index 06a28ad8fa39a5..9f87ccf677422c 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataModule.cs @@ -234,7 +234,7 @@ int IXCLRDataModule.StartEnumExtents(ulong* handle) { if (contract.TryGetLoadedImageContents(moduleHandle, out TargetPointer baseAddress, out uint size, out _)) { - _extents[0].baseAddress = baseAddress; + _extents[0].baseAddress = baseAddress.ToClrDataAddress(_target); _extents[0].length = size; _extents[0].type = 0x0; // CLRDATA_MODULE_PE_FILE } @@ -349,7 +349,7 @@ private int DacPrivateRequestGetModuleData(uint inBufferSize, byte* inBuffer, ui bool isReflectionEmit = (contract.GetFlags(moduleHandle) & ModuleFlags.ReflectionEmit) != 0; - getModuleData->PEAssembly = _address; + getModuleData->PEAssembly = _address.ToClrDataAddress(_target); getModuleData->IsDynamic = isReflectionEmit ? 1u : 0u; if (peAssembly != TargetPointer.Null) @@ -365,7 +365,7 @@ private int DacPrivateRequestGetModuleData(uint inBufferSize, byte* inBuffer, ui } contract.TryGetLoadedImageContents(moduleHandle, out TargetPointer baseAddress, out uint size, out uint flags); - getModuleData->LoadedPEAddress = baseAddress; + getModuleData->LoadedPEAddress = baseAddress.ToClrDataAddress(_target); getModuleData->LoadedPESize = size; // Can not get the assembly layout for a dynamic module @@ -377,7 +377,7 @@ private int DacPrivateRequestGetModuleData(uint inBufferSize, byte* inBuffer, ui if (contract.TryGetSymbolStream(moduleHandle, out TargetPointer symbolBuffer, out uint symbolBufferSize)) { - getModuleData->InMemoryPdbAddress = symbolBuffer; + getModuleData->InMemoryPdbAddress = symbolBuffer.ToClrDataAddress(_target); getModuleData->InMemoryPdbSize = symbolBufferSize; } diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ConversionExtensions.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ConversionExtensions.cs new file mode 100644 index 00000000000000..8bcaa85234a9bb --- /dev/null +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ConversionExtensions.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Legacy; + +internal static class ConversionExtensions +{ + /// + /// Converts a TargetPointer to a ClrDataAddress using sign extension if required. + /// + public static ClrDataAddress ToClrDataAddress(this TargetPointer address, Target target) + { + if (target.PointerSize == sizeof(ulong)) + { + return address.Value; + } + else + { + return (ulong)(int)address.Value; + } + } + + /// + /// Converts a ClrDataAddress to a TargetPointer, ensuring the address is within the valid range for the target platform. + /// + public static TargetPointer ToTargetPointer(this ClrDataAddress address, Target target) + { + if (target.PointerSize == sizeof(ulong)) + { + return new TargetPointer(address); + } + else + { + long signedAddr = (long)address.Value; + if (signedAddr > int.MaxValue || signedAddr < int.MinValue) + { + throw new ArgumentException(nameof(address), "ClrDataAddress out of range for the target platform."); + } + return new TargetPointer((ulong)address); + } + } + + /// + /// Converts a ClrDataAddress to a TargetCodePointer, ensuring the address is within the valid range for the target platform. + /// + public static TargetCodePointer ToTargetCodePointer(this ClrDataAddress address, Target target) + { + if (target.PointerSize == sizeof(ulong)) + { + return new TargetCodePointer(address); + } + else + { + long signedAddr = (long)address.Value; + if (signedAddr > int.MaxValue || signedAddr < int.MinValue) + { + throw new ArgumentException(nameof(address), "ClrDataAddress out of range for the target platform."); + } + return new TargetCodePointer((ulong)address); + } + } +} diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs index 8dc935f8151f6c..289130ed21ad5e 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs @@ -18,9 +18,9 @@ internal struct DacpThreadStoreData public int backgroundThreadCount; public int pendingThreadCount; public int deadThreadCount; - public ulong firstThread; - public ulong finalizerThread; - public ulong gcThread; + public ClrDataAddress firstThread; + public ClrDataAddress finalizerThread; + public ClrDataAddress gcThread; public int fHostConfig; // Uses hosting flags defined above }; @@ -30,27 +30,27 @@ internal struct DacpThreadData public int osThreadId; public int state; public uint preemptiveGCDisabled; - public ulong allocContextPtr; - public ulong allocContextLimit; - public ulong context; - public ulong domain; - public ulong pFrame; + public ClrDataAddress allocContextPtr; + public ClrDataAddress allocContextLimit; + public ClrDataAddress context; + public ClrDataAddress domain; + public ClrDataAddress pFrame; public int lockCount; - public ulong firstNestedException; // Pass this pointer to DacpNestedExceptionInfo - public ulong teb; - public ulong fiberData; - public ulong lastThrownObjectHandle; - public ulong nextThread; + public ClrDataAddress firstNestedException; // Pass this pointer to DacpNestedExceptionInfo + public ClrDataAddress teb; + public ClrDataAddress fiberData; + public ClrDataAddress lastThrownObjectHandle; + public ClrDataAddress nextThread; } internal struct DacpModuleData { - public ulong Address; - public ulong PEAssembly; // Actually the module address in .NET 9+ - public ulong ilBase; - public ulong metadataStart; + public ClrDataAddress Address; + public ClrDataAddress PEAssembly; // Actually the module address in .NET 9+ + public ClrDataAddress ilBase; + public ClrDataAddress metadataStart; public ulong metadataSize; - public ulong Assembly; // Assembly pointer + public ClrDataAddress Assembly; // Assembly pointer public uint isReflection; public uint isPEFile; @@ -59,16 +59,16 @@ internal struct DacpModuleData public uint dwTransientFlags; - public ulong TypeDefToMethodTableMap; - public ulong TypeRefToMethodTableMap; - public ulong MethodDefToDescMap; - public ulong FieldDefToDescMap; - public ulong MemberRefToDescMap; - public ulong FileReferencesMap; - public ulong ManifestModuleReferencesMap; + public ClrDataAddress TypeDefToMethodTableMap; + public ClrDataAddress TypeRefToMethodTableMap; + public ClrDataAddress MethodDefToDescMap; + public ClrDataAddress FieldDefToDescMap; + public ClrDataAddress MemberRefToDescMap; + public ClrDataAddress FileReferencesMap; + public ClrDataAddress ManifestModuleReferencesMap; - public ulong LoaderAllocator; - public ulong ThunkHeap; + public ClrDataAddress LoaderAllocator; + public ClrDataAddress ThunkHeap; public ulong dwModuleIndex; // Always 0 - .NET no longer has this } @@ -76,9 +76,9 @@ internal struct DacpModuleData internal struct DacpMethodTableData { public int bIsFree; // everything else is NULL if this is true. - public ulong module; - public ulong klass; - public ulong parentMethodTable; + public ClrDataAddress module; + public ClrDataAddress klass; + public ClrDataAddress parentMethodTable; public ushort wNumInterfaces; public ushort wNumMethods; public ushort wNumVtableSlots; @@ -103,28 +103,28 @@ internal enum DacpObjectType internal struct DacpObjectData { - public ulong MethodTable; + public ClrDataAddress MethodTable; public DacpObjectType ObjectType; public ulong Size; - public ulong ElementTypeHandle; + public ClrDataAddress ElementTypeHandle; public uint ElementType; public uint dwRank; public ulong dwNumComponents; public ulong dwComponentSize; - public ulong ArrayDataPtr; - public ulong ArrayBoundsPtr; - public ulong ArrayLowerBoundsPtr; + public ClrDataAddress ArrayDataPtr; + public ClrDataAddress ArrayBoundsPtr; + public ClrDataAddress ArrayLowerBoundsPtr; public ulong RCW; public ulong CCW; } internal struct DacpUsefulGlobalsData { - public ulong ArrayMethodTable; - public ulong StringMethodTable; - public ulong ObjectMethodTable; - public ulong ExceptionMethodTable; - public ulong FreeMethodTable; + public ClrDataAddress ArrayMethodTable; + public ClrDataAddress StringMethodTable; + public ClrDataAddress ObjectMethodTable; + public ClrDataAddress ExceptionMethodTable; + public ClrDataAddress FreeMethodTable; } #pragma warning restore CS0649 // Field is never assigned to, and will always have its default value @@ -139,9 +139,9 @@ public enum Flags : uint kReverted = 3, }; - public ulong /*CLRDATA_ADDRESS*/ rejitID; + public ClrDataAddress rejitID; public Flags flags; /* = Flags::kUnknown*/ - public ulong /*CLRDATA_ADDRESS*/ NativeCodeAddr; + public ClrDataAddress NativeCodeAddr; }; internal struct DacpMethodDescData @@ -149,22 +149,22 @@ internal struct DacpMethodDescData public int bHasNativeCode; public int bIsDynamic; public ushort wSlotNumber; - public ulong /*CLRDATA_ADDRESS*/ NativeCodeAddr; + public ClrDataAddress NativeCodeAddr; // Useful for breaking when a method is jitted. - public ulong /*CLRDATA_ADDRESS*/ AddressOfNativeCodeSlot; + public ClrDataAddress AddressOfNativeCodeSlot; - public ulong /*CLRDATA_ADDRESS*/ MethodDescPtr; - public ulong /*CLRDATA_ADDRESS*/ MethodTablePtr; - public ulong /*CLRDATA_ADDRESS*/ ModulePtr; + public ClrDataAddress MethodDescPtr; + public ClrDataAddress MethodTablePtr; + public ClrDataAddress ModulePtr; public uint /*mdToken*/ MDToken; - public ulong /*CLRDATA_ADDRESS*/ GCInfo; - public ulong /*CLRDATA_ADDRESS*/ GCStressCodeCopy; + public ClrDataAddress GCInfo; + public ClrDataAddress GCStressCodeCopy; // This is only valid if bIsDynamic is true - public ulong /*CLRDATA_ADDRESS*/ managedDynamicMethodObject; + public ClrDataAddress managedDynamicMethodObject; - public ulong /*CLRDATA_ADDRESS*/ requestedIP; + public ClrDataAddress requestedIP; // Gives info for the single currently active version of a method public DacpReJitData rejitDataCurrent; @@ -192,149 +192,149 @@ internal unsafe partial interface ISOSDacInterface [PreserveSig] int GetAppDomainStoreData(/*struct DacpAppDomainStoreData*/ void* data); [PreserveSig] - int GetAppDomainList(uint count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ulong[] values, uint* pNeeded); + int GetAppDomainList(uint count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ClrDataAddress[] values, uint* pNeeded); [PreserveSig] - int GetAppDomainData(ulong addr, /*struct DacpAppDomainData*/ void* data); + int GetAppDomainData(ClrDataAddress addr, /*struct DacpAppDomainData*/ void* data); [PreserveSig] - int GetAppDomainName(ulong addr, uint count, char* name, uint* pNeeded); + int GetAppDomainName(ClrDataAddress addr, uint count, char* name, uint* pNeeded); [PreserveSig] - int GetDomainFromContext(ulong context, ulong* domain); + int GetDomainFromContext(ClrDataAddress context, ClrDataAddress* domain); // Assemblies [PreserveSig] - int GetAssemblyList(ulong appDomain, int count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ulong[]? values, int* pNeeded); + int GetAssemblyList(ClrDataAddress appDomain, int count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ClrDataAddress[]? values, int* pNeeded); [PreserveSig] - int GetAssemblyData(ulong baseDomainPtr, ulong assembly, /*struct DacpAssemblyData*/ void* data); + int GetAssemblyData(ClrDataAddress baseDomainPtr, ClrDataAddress assembly, /*struct DacpAssemblyData*/ void* data); [PreserveSig] - int GetAssemblyName(ulong assembly, uint count, char* name, uint* pNeeded); + int GetAssemblyName(ClrDataAddress assembly, uint count, char* name, uint* pNeeded); // Modules [PreserveSig] - int GetModule(ulong addr, out IXCLRDataModule? mod); + int GetModule(ClrDataAddress addr, out IXCLRDataModule? mod); [PreserveSig] - int GetModuleData(ulong moduleAddr, DacpModuleData* data); + int GetModuleData(ClrDataAddress moduleAddr, DacpModuleData* data); [PreserveSig] - int TraverseModuleMap(/*ModuleMapType*/ int mmt, ulong moduleAddr, /*MODULEMAPTRAVERSE*/ void* pCallback, void* token); + int TraverseModuleMap(/*ModuleMapType*/ int mmt, ClrDataAddress moduleAddr, /*MODULEMAPTRAVERSE*/ void* pCallback, void* token); [PreserveSig] - int GetAssemblyModuleList(ulong assembly, uint count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ulong[] modules, uint* pNeeded); + int GetAssemblyModuleList(ClrDataAddress assembly, uint count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ClrDataAddress[] modules, uint* pNeeded); [PreserveSig] - int GetILForModule(ulong moduleAddr, int rva, ulong* il); + int GetILForModule(ClrDataAddress moduleAddr, int rva, ClrDataAddress* il); // Threads [PreserveSig] - int GetThreadData(ulong thread, DacpThreadData *data); + int GetThreadData(ClrDataAddress thread, DacpThreadData *data); [PreserveSig] - int GetThreadFromThinlockID(uint thinLockId, ulong* pThread); + int GetThreadFromThinlockID(uint thinLockId, ClrDataAddress* pThread); [PreserveSig] - int GetStackLimits(ulong threadPtr, ulong* lower, ulong* upper, ulong* fp); + int GetStackLimits(ClrDataAddress threadPtr, ClrDataAddress* lower, ClrDataAddress* upper, ClrDataAddress* fp); // MethodDescs [PreserveSig] - int GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDescData* data, uint cRevertedRejitVersions, DacpReJitData* rgRevertedRejitData, uint* pcNeededRevertedRejitData); + int GetMethodDescData(ClrDataAddress methodDesc, ClrDataAddress ip, DacpMethodDescData* data, uint cRevertedRejitVersions, DacpReJitData* rgRevertedRejitData, uint* pcNeededRevertedRejitData); [PreserveSig] - int GetMethodDescPtrFromIP(ulong ip, ulong* ppMD); + int GetMethodDescPtrFromIP(ClrDataAddress ip, ClrDataAddress* ppMD); [PreserveSig] - int GetMethodDescName(ulong methodDesc, uint count, char* name, uint* pNeeded); + int GetMethodDescName(ClrDataAddress methodDesc, uint count, char* name, uint* pNeeded); [PreserveSig] - int GetMethodDescPtrFromFrame(ulong frameAddr, ulong* ppMD); + int GetMethodDescPtrFromFrame(ClrDataAddress frameAddr, ClrDataAddress* ppMD); [PreserveSig] - int GetMethodDescFromToken(ulong moduleAddr, /*mdToken*/ uint token, ulong* methodDesc); + int GetMethodDescFromToken(ClrDataAddress moduleAddr, /*mdToken*/ uint token, ClrDataAddress* methodDesc); [PreserveSig] - int GetMethodDescTransparencyData(ulong methodDesc, /*struct DacpMethodDescTransparencyData*/ void* data); + int GetMethodDescTransparencyData(ClrDataAddress methodDesc, /*struct DacpMethodDescTransparencyData*/ void* data); // JIT Data [PreserveSig] - int GetCodeHeaderData(ulong ip, /*struct DacpCodeHeaderData*/ void* data); + int GetCodeHeaderData(ClrDataAddress ip, /*struct DacpCodeHeaderData*/ void* data); [PreserveSig] int GetJitManagerList(uint count, /*struct DacpJitManagerInfo*/ void* managers, uint* pNeeded); [PreserveSig] - int GetJitHelperFunctionName(ulong ip, uint count, byte* name, uint* pNeeded); + int GetJitHelperFunctionName(ClrDataAddress ip, uint count, byte* name, uint* pNeeded); [PreserveSig] - int GetJumpThunkTarget(/*T_CONTEXT*/void* ctx, ulong* targetIP, ulong* targetMD); + int GetJumpThunkTarget(/*T_CONTEXT*/void* ctx, ClrDataAddress* targetIP, ClrDataAddress* targetMD); // ThreadPool [PreserveSig] int GetThreadpoolData(/*struct DacpThreadpoolData*/ void* data); [PreserveSig] - int GetWorkRequestData(ulong addrWorkRequest, /*struct DacpWorkRequestData*/ void* data); + int GetWorkRequestData(ClrDataAddress addrWorkRequest, /*struct DacpWorkRequestData*/ void* data); [PreserveSig] - int GetHillClimbingLogEntry(ulong addr, /*struct DacpHillClimbingLogEntry*/ void* data); + int GetHillClimbingLogEntry(ClrDataAddress addr, /*struct DacpHillClimbingLogEntry*/ void* data); // Objects [PreserveSig] - int GetObjectData(ulong objAddr, DacpObjectData* data); + int GetObjectData(ClrDataAddress objAddr, DacpObjectData* data); [PreserveSig] - int GetObjectStringData(ulong obj, uint count, char* stringData, uint* pNeeded); + int GetObjectStringData(ClrDataAddress obj, uint count, char* stringData, uint* pNeeded); [PreserveSig] - int GetObjectClassName(ulong obj, uint count, char* className, uint* pNeeded); + int GetObjectClassName(ClrDataAddress obj, uint count, char* className, uint* pNeeded); // MethodTable [PreserveSig] - int GetMethodTableName(ulong mt, uint count, char* mtName, uint* pNeeded); + int GetMethodTableName(ClrDataAddress mt, uint count, char* mtName, uint* pNeeded); [PreserveSig] - int GetMethodTableData(ulong mt, DacpMethodTableData* data); + int GetMethodTableData(ClrDataAddress mt, DacpMethodTableData* data); [PreserveSig] - int GetMethodTableSlot(ulong mt, uint slot, ulong* value); + int GetMethodTableSlot(ClrDataAddress mt, uint slot, ClrDataAddress* value); [PreserveSig] - int GetMethodTableFieldData(ulong mt, /*struct DacpMethodTableFieldData*/ void* data); + int GetMethodTableFieldData(ClrDataAddress mt, /*struct DacpMethodTableFieldData*/ void* data); [PreserveSig] - int GetMethodTableTransparencyData(ulong mt, /*struct DacpMethodTableTransparencyData*/ void* data); + int GetMethodTableTransparencyData(ClrDataAddress mt, /*struct DacpMethodTableTransparencyData*/ void* data); // EEClass [PreserveSig] - int GetMethodTableForEEClass(ulong eeClass, ulong* value); + int GetMethodTableForEEClass(ClrDataAddress eeClass, ClrDataAddress* value); // FieldDesc [PreserveSig] - int GetFieldDescData(ulong fieldDesc, /*struct DacpFieldDescData*/ void* data); + int GetFieldDescData(ClrDataAddress fieldDesc, /*struct DacpFieldDescData*/ void* data); // Frames [PreserveSig] - int GetFrameName(ulong vtable, uint count, char* frameName, uint* pNeeded); + int GetFrameName(ClrDataAddress vtable, uint count, char* frameName, uint* pNeeded); // PEFiles [PreserveSig] - int GetPEFileBase(ulong addr, ulong* peBase); + int GetPEFileBase(ClrDataAddress addr, ClrDataAddress* peBase); [PreserveSig] - int GetPEFileName(ulong addr, uint count, char* fileName, uint* pNeeded); + int GetPEFileName(ClrDataAddress addr, uint count, char* fileName, uint* pNeeded); // GC [PreserveSig] int GetGCHeapData(/*struct DacpGcHeapData*/ void* data); [PreserveSig] - int GetGCHeapList(uint count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ulong[] heaps, uint* pNeeded); // svr only + int GetGCHeapList(uint count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ClrDataAddress[] heaps, uint* pNeeded); // svr only [PreserveSig] - int GetGCHeapDetails(ulong heap, /*struct DacpGcHeapDetails */ void* details); // wks only + int GetGCHeapDetails(ClrDataAddress heap, /*struct DacpGcHeapDetails */ void* details); // wks only [PreserveSig] int GetGCHeapStaticData(/*struct DacpGcHeapDetails */ void* data); [PreserveSig] - int GetHeapSegmentData(ulong seg, /*struct DacpHeapSegmentData */ void* data); + int GetHeapSegmentData(ClrDataAddress seg, /*struct DacpHeapSegmentData */ void* data); [PreserveSig] - int GetOOMData(ulong oomAddr, /*struct DacpOomData */ void* data); + int GetOOMData(ClrDataAddress oomAddr, /*struct DacpOomData */ void* data); [PreserveSig] int GetOOMStaticData(/*struct DacpOomData */ void* data); [PreserveSig] - int GetHeapAnalyzeData(ulong addr, /*struct DacpGcHeapAnalyzeData */ void* data); + int GetHeapAnalyzeData(ClrDataAddress addr, /*struct DacpGcHeapAnalyzeData */ void* data); [PreserveSig] int GetHeapAnalyzeStaticData(/*struct DacpGcHeapAnalyzeData */ void* data); // DomainLocal [PreserveSig] - int GetDomainLocalModuleData(ulong addr, /*struct DacpDomainLocalModuleData */ void* data); + int GetDomainLocalModuleData(ClrDataAddress addr, /*struct DacpDomainLocalModuleData */ void* data); [PreserveSig] - int GetDomainLocalModuleDataFromAppDomain(ulong appDomainAddr, int moduleID, /*struct DacpDomainLocalModuleData */ void* data); + int GetDomainLocalModuleDataFromAppDomain(ClrDataAddress appDomainAddr, int moduleID, /*struct DacpDomainLocalModuleData */ void* data); [PreserveSig] - int GetDomainLocalModuleDataFromModule(ulong moduleAddr, /*struct DacpDomainLocalModuleData */ void* data); + int GetDomainLocalModuleDataFromModule(ClrDataAddress moduleAddr, /*struct DacpDomainLocalModuleData */ void* data); // ThreadLocal [PreserveSig] - int GetThreadLocalModuleData(ulong thread, uint index, /*struct DacpThreadLocalModuleData */ void* data); + int GetThreadLocalModuleData(ClrDataAddress thread, uint index, /*struct DacpThreadLocalModuleData */ void* data); // SyncBlock [PreserveSig] int GetSyncBlockData(uint number, /*struct DacpSyncBlockData */ void* data); [PreserveSig] - int GetSyncBlockCleanupData(ulong addr, /*struct DacpSyncBlockCleanupData */ void* data); + int GetSyncBlockCleanupData(ClrDataAddress addr, /*struct DacpSyncBlockCleanupData */ void* data); // Handles [PreserveSig] @@ -346,27 +346,27 @@ internal unsafe partial interface ISOSDacInterface // EH [PreserveSig] - int TraverseEHInfo(ulong ip, /*DUMPEHINFO*/ void* pCallback, void* token); + int TraverseEHInfo(ClrDataAddress ip, /*DUMPEHINFO*/ void* pCallback, void* token); [PreserveSig] - int GetNestedExceptionData(ulong exception, ulong* exceptionObject, ulong* nextNestedException); + int GetNestedExceptionData(ClrDataAddress exception, ClrDataAddress* exceptionObject, ClrDataAddress* nextNestedException); // StressLog [PreserveSig] - int GetStressLogAddress(ulong* stressLog); + int GetStressLogAddress(ClrDataAddress* stressLog); // Heaps [PreserveSig] - int TraverseLoaderHeap(ulong loaderHeapAddr, /*VISITHEAP*/ void* pCallback); + int TraverseLoaderHeap(ClrDataAddress loaderHeapAddr, /*VISITHEAP*/ void* pCallback); [PreserveSig] - int GetCodeHeapList(ulong jitManager, uint count, /*struct DacpJitCodeHeapInfo*/ void* codeHeaps, uint* pNeeded); + int GetCodeHeapList(ClrDataAddress jitManager, uint count, /*struct DacpJitCodeHeapInfo*/ void* codeHeaps, uint* pNeeded); [PreserveSig] - int TraverseVirtCallStubHeap(ulong pAppDomain, /*VCSHeapType*/ int heaptype, /*VISITHEAP*/ void* pCallback); + int TraverseVirtCallStubHeap(ClrDataAddress pAppDomain, /*VCSHeapType*/ int heaptype, /*VISITHEAP*/ void* pCallback); // Other [PreserveSig] int GetUsefulGlobals(DacpUsefulGlobalsData* data); [PreserveSig] - int GetClrWatsonBuckets(ulong thread, void* pGenericModeBlock); + int GetClrWatsonBuckets(ClrDataAddress thread, void* pGenericModeBlock); [PreserveSig] int GetTLSIndex(uint* pIndex); [PreserveSig] @@ -374,15 +374,15 @@ internal unsafe partial interface ISOSDacInterface // COM [PreserveSig] - int GetRCWData(ulong addr, /*struct DacpRCWData */ void* data); + int GetRCWData(ClrDataAddress addr, /*struct DacpRCWData */ void* data); [PreserveSig] - int GetRCWInterfaces(ulong rcw, uint count, /*struct DacpCOMInterfacePointerData*/ void* interfaces, uint* pNeeded); + int GetRCWInterfaces(ClrDataAddress rcw, uint count, /*struct DacpCOMInterfacePointerData*/ void* interfaces, uint* pNeeded); [PreserveSig] - int GetCCWData(ulong ccw, /*struct DacpCCWData */ void* data); + int GetCCWData(ClrDataAddress ccw, /*struct DacpCCWData */ void* data); [PreserveSig] - int GetCCWInterfaces(ulong ccw, uint count, /*struct DacpCOMInterfacePointerData*/ void* interfaces, uint* pNeeded); + int GetCCWInterfaces(ClrDataAddress ccw, uint count, /*struct DacpCOMInterfacePointerData*/ void* interfaces, uint* pNeeded); [PreserveSig] - int TraverseRCWCleanupList(ulong cleanupListPtr, /*VISITRCWFORCLEANUP*/ void* pCallback, void* token); + int TraverseRCWCleanupList(ClrDataAddress cleanupListPtr, /*VISITRCWFORCLEANUP*/ void* pCallback, void* token); // GC Reference Functions @@ -395,38 +395,38 @@ internal unsafe partial interface ISOSDacInterface int GetRegisterName(int regName, uint count, char* buffer, uint* pNeeded); [PreserveSig] - int GetThreadAllocData(ulong thread, /*struct DacpAllocData */ void* data); + int GetThreadAllocData(ClrDataAddress thread, /*struct DacpAllocData */ void* data); [PreserveSig] int GetHeapAllocData(uint count, /*struct DacpGenerationAllocData */ void* data, uint* pNeeded); // For BindingDisplay plugin [PreserveSig] - int GetFailedAssemblyList(ulong appDomain, int count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ulong[] values, uint* pNeeded); + int GetFailedAssemblyList(ClrDataAddress appDomain, int count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ClrDataAddress[] values, uint* pNeeded); [PreserveSig] - int GetPrivateBinPaths(ulong appDomain, int count, char* paths, uint* pNeeded); + int GetPrivateBinPaths(ClrDataAddress appDomain, int count, char* paths, uint* pNeeded); [PreserveSig] - int GetAssemblyLocation(ulong assembly, int count, char* location, uint* pNeeded); + int GetAssemblyLocation(ClrDataAddress assembly, int count, char* location, uint* pNeeded); [PreserveSig] - int GetAppDomainConfigFile(ulong appDomain, int count, char* configFile, uint* pNeeded); + int GetAppDomainConfigFile(ClrDataAddress appDomain, int count, char* configFile, uint* pNeeded); [PreserveSig] - int GetApplicationBase(ulong appDomain, int count, char* appBase, uint* pNeeded); + int GetApplicationBase(ClrDataAddress appDomain, int count, char* appBase, uint* pNeeded); [PreserveSig] - int GetFailedAssemblyData(ulong assembly, uint* pContext, int* pResult); + int GetFailedAssemblyData(ClrDataAddress assembly, uint* pContext, int* pResult); [PreserveSig] - int GetFailedAssemblyLocation(ulong assesmbly, uint count, char* location, uint* pNeeded); + int GetFailedAssemblyLocation(ClrDataAddress assesmbly, uint count, char* location, uint* pNeeded); [PreserveSig] - int GetFailedAssemblyDisplayName(ulong assembly, uint count, char* name, uint* pNeeded); + int GetFailedAssemblyDisplayName(ClrDataAddress assembly, uint count, char* name, uint* pNeeded); }; #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value internal struct DacpExceptionObjectData { - public ulong Message; - public ulong InnerException; - public ulong StackTrace; - public ulong WatsonBuckets; - public ulong StackTraceString; - public ulong RemoteStackTraceString; + public ClrDataAddress Message; + public ClrDataAddress InnerException; + public ClrDataAddress StackTrace; + public ClrDataAddress WatsonBuckets; + public ClrDataAddress StackTraceString; + public ClrDataAddress RemoteStackTraceString; public int HResult; public int XCode; } @@ -437,9 +437,9 @@ internal struct DacpExceptionObjectData internal unsafe partial interface ISOSDacInterface2 { [PreserveSig] - int GetObjectExceptionData(ulong objectAddress, DacpExceptionObjectData* data); + int GetObjectExceptionData(ClrDataAddress objectAddress, DacpExceptionObjectData* data); [PreserveSig] - int IsRCWDCOMProxy(ulong rcwAddress, int* inDCOMProxy); + int IsRCWDCOMProxy(ClrDataAddress rcwAddress, int* inDCOMProxy); } [GeneratedComInterface] @@ -447,7 +447,7 @@ internal unsafe partial interface ISOSDacInterface2 internal unsafe partial interface ISOSDacInterface3 { [PreserveSig] - int GetGCInterestingInfoData(ulong interestingInfoAddr, /*struct DacpGCInterestingInfoData*/ void* data); + int GetGCInterestingInfoData(ClrDataAddress interestingInfoAddr, /*struct DacpGCInterestingInfoData*/ void* data); [PreserveSig] int GetGCInterestingInfoStaticData(/*struct DacpGCInterestingInfoData*/ void* data); [PreserveSig] @@ -459,7 +459,7 @@ internal unsafe partial interface ISOSDacInterface3 internal unsafe partial interface ISOSDacInterface4 { [PreserveSig] - int GetClrNotification([In, Out, MarshalUsing(CountElementName = nameof(count))] ulong[] arguments, int count, int* pNeeded); + int GetClrNotification([In, Out, MarshalUsing(CountElementName = nameof(count))] ClrDataAddress[] arguments, int count, int* pNeeded); }; [GeneratedComInterface] @@ -467,7 +467,7 @@ internal unsafe partial interface ISOSDacInterface4 internal unsafe partial interface ISOSDacInterface5 { [PreserveSig] - int GetTieredVersions(ulong methodDesc, int rejitId, /*struct DacpTieredVersionData*/void* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs); + int GetTieredVersions(ClrDataAddress methodDesc, int rejitId, /*struct DacpTieredVersionData*/void* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs); }; [GeneratedComInterface] @@ -475,7 +475,7 @@ internal unsafe partial interface ISOSDacInterface5 internal unsafe partial interface ISOSDacInterface6 { [PreserveSig] - int GetMethodTableCollectibleData(ulong mt, /*struct DacpMethodTableCollectibleData*/ void* data); + int GetMethodTableCollectibleData(ClrDataAddress mt, /*struct DacpMethodTableCollectibleData*/ void* data); }; [GeneratedComInterface] @@ -483,13 +483,13 @@ internal unsafe partial interface ISOSDacInterface6 internal unsafe partial interface ISOSDacInterface7 { [PreserveSig] - int GetPendingReJITID(ulong methodDesc, int* pRejitId); + int GetPendingReJITID(ClrDataAddress methodDesc, int* pRejitId); [PreserveSig] - int GetReJITInformation(ulong methodDesc, int rejitId, /*struct DacpReJitData2*/ void* pRejitData); + int GetReJITInformation(ClrDataAddress methodDesc, int rejitId, /*struct DacpReJitData2*/ void* pRejitData); [PreserveSig] - int GetProfilerModifiedILInformation(ulong methodDesc, /*struct DacpProfilerILData*/ void* pILData); + int GetProfilerModifiedILInformation(ClrDataAddress methodDesc, /*struct DacpProfilerILData*/ void* pILData); [PreserveSig] - int GetMethodsWithProfilerModifiedIL(ulong mod, ulong* methodDescs, int cMethodDescs, int* pcMethodDescs); + int GetMethodsWithProfilerModifiedIL(ClrDataAddress mod, ClrDataAddress* methodDescs, int cMethodDescs, int* pcMethodDescs); }; [GeneratedComInterface] @@ -503,16 +503,16 @@ internal unsafe partial interface ISOSDacInterface8 [PreserveSig] int GetGenerationTable(uint cGenerations, /*struct DacpGenerationData*/ void* pGenerationData, uint* pNeeded); [PreserveSig] - int GetFinalizationFillPointers(uint cFillPointers, ulong* pFinalizationFillPointers, uint* pNeeded); + int GetFinalizationFillPointers(uint cFillPointers, ClrDataAddress* pFinalizationFillPointers, uint* pNeeded); // SVR [PreserveSig] - int GetGenerationTableSvr(ulong heapAddr, uint cGenerations, /*struct DacpGenerationData*/ void* pGenerationData, uint* pNeeded); + int GetGenerationTableSvr(ClrDataAddress heapAddr, uint cGenerations, /*struct DacpGenerationData*/ void* pGenerationData, uint* pNeeded); [PreserveSig] - int GetFinalizationFillPointersSvr(ulong heapAddr, uint cFillPointers, ulong* pFinalizationFillPointers, uint* pNeeded); + int GetFinalizationFillPointersSvr(ClrDataAddress heapAddr, uint cFillPointers, ClrDataAddress* pFinalizationFillPointers, uint* pNeeded); [PreserveSig] - int GetAssemblyLoadContext(ulong methodTable, ulong* assemblyLoadContext); + int GetAssemblyLoadContext(ClrDataAddress methodTable, ClrDataAddress* assemblyLoadContext); } [GeneratedComInterface] @@ -527,15 +527,15 @@ internal partial interface ISOSDacInterface9 internal unsafe partial interface ISOSDacInterface10 { [PreserveSig] - int GetObjectComWrappersData(ulong objAddr, ulong* rcw, uint count, ulong* mowList, uint* pNeeded); + int GetObjectComWrappersData(ClrDataAddress objAddr, ClrDataAddress* rcw, uint count, ClrDataAddress* mowList, uint* pNeeded); [PreserveSig] - int IsComWrappersCCW(ulong ccw, Interop.BOOL* isComWrappersCCW); + int IsComWrappersCCW(ClrDataAddress ccw, Interop.BOOL* isComWrappersCCW); [PreserveSig] - int GetComWrappersCCWData(ulong ccw, ulong* managedObject, int* refCount); + int GetComWrappersCCWData(ClrDataAddress ccw, ClrDataAddress* managedObject, int* refCount); [PreserveSig] - int IsComWrappersRCW(ulong rcw, Interop.BOOL* isComWrappersRCW); + int IsComWrappersRCW(ClrDataAddress rcw, Interop.BOOL* isComWrappersRCW); [PreserveSig] - int GetComWrappersRCWData(ulong rcw, ulong* identity); + int GetComWrappersRCWData(ClrDataAddress rcw, ClrDataAddress* identity); } [GeneratedComInterface] @@ -543,9 +543,9 @@ internal unsafe partial interface ISOSDacInterface10 internal unsafe partial interface ISOSDacInterface11 { [PreserveSig] - int IsTrackedType(ulong objAddr, Interop.BOOL* isTrackedType, Interop.BOOL* hasTaggedMemory); + int IsTrackedType(ClrDataAddress objAddr, Interop.BOOL* isTrackedType, Interop.BOOL* hasTaggedMemory); [PreserveSig] - int GetTaggedMemory(ulong objAddr, ulong* taggedMemory, nuint* taggedMemorySizeInBytes); + int GetTaggedMemory(ClrDataAddress objAddr, ClrDataAddress* taggedMemory, nuint* taggedMemorySizeInBytes); } [GeneratedComInterface] @@ -553,7 +553,7 @@ internal unsafe partial interface ISOSDacInterface11 internal unsafe partial interface ISOSDacInterface12 { [PreserveSig] - int GetGlobalAllocationContext(ulong* allocPtr, ulong* allocLimit); + int GetGlobalAllocationContext(ClrDataAddress* allocPtr, ClrDataAddress* allocLimit); } [GeneratedComInterface] @@ -561,13 +561,13 @@ internal unsafe partial interface ISOSDacInterface12 internal unsafe partial interface ISOSDacInterface13 { [PreserveSig] - int TraverseLoaderHeap(ulong loaderHeapAddr, /*LoaderHeapKind*/ int kind, /*VISITHEAP*/ delegate* unmanaged pCallback); + int TraverseLoaderHeap(ClrDataAddress loaderHeapAddr, /*LoaderHeapKind*/ int kind, /*VISITHEAP*/ delegate* unmanaged pCallback); [PreserveSig] - int GetDomainLoaderAllocator(ulong domainAddress, ulong* pLoaderAllocator); + int GetDomainLoaderAllocator(ClrDataAddress domainAddress, ClrDataAddress* pLoaderAllocator); [PreserveSig] int GetLoaderAllocatorHeapNames(int count, char** ppNames, int* pNeeded); [PreserveSig] - int GetLoaderAllocatorHeaps(ulong loaderAllocator, int count, ulong* pLoaderHeaps, /*LoaderHeapKind*/ int* pKinds, int* pNeeded); + int GetLoaderAllocatorHeaps(ClrDataAddress loaderAllocator, int count, ClrDataAddress* pLoaderHeaps, /*LoaderHeapKind*/ int* pKinds, int* pNeeded); [PreserveSig] int GetHandleTableMemoryRegions(/*ISOSMemoryEnum*/ void** ppEnum); [PreserveSig] @@ -583,11 +583,11 @@ internal unsafe partial interface ISOSDacInterface13 internal unsafe partial interface ISOSDacInterface14 { [PreserveSig] - int GetStaticBaseAddress(ulong methodTable, ulong* nonGCStaticsAddress, ulong* GCStaticsAddress); + int GetStaticBaseAddress(ClrDataAddress methodTable, ClrDataAddress* nonGCStaticsAddress, ClrDataAddress* GCStaticsAddress); [PreserveSig] - int GetThreadStaticBaseAddress(ulong methodTable, ulong thread, ulong* nonGCStaticsAddress, ulong* GCStaticsAddress); + int GetThreadStaticBaseAddress(ClrDataAddress methodTable, ClrDataAddress thread, ClrDataAddress* nonGCStaticsAddress, ClrDataAddress* GCStaticsAddress); [PreserveSig] - int GetMethodTableInitializationFlags(ulong methodTable, /*MethodTableInitializationFlags*/ int* initializationStatus); + int GetMethodTableInitializationFlags(ClrDataAddress methodTable, /*MethodTableInitializationFlags*/ int* initializationStatus); } [GeneratedComInterface] @@ -595,5 +595,5 @@ internal unsafe partial interface ISOSDacInterface14 internal unsafe partial interface ISOSDacInterface15 { [PreserveSig] - int GetMethodTableSlotEnumerator(ulong mt, /*ISOSMethodEnum*/void** enumerator); + int GetMethodTableSlotEnumerator(ClrDataAddress mt, /*ISOSMethodEnum*/void** enumerator); } diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/IXCLRData.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/IXCLRData.cs index 2212af540bb44e..231c0977a20686 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/IXCLRData.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/IXCLRData.cs @@ -12,7 +12,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Legacy; internal struct CLRDataModuleExtent { - public ulong /* CLRDATA_ADDRESS */ baseAddress; + public ClrDataAddress baseAddress; public uint length; public uint /* CLRDataModuleExtentType */ type; } @@ -22,10 +22,10 @@ internal struct DacpGetModuleData public uint IsDynamic; public uint IsInMemory; public uint IsFileLayout; - public ulong /* CLRDATA_ADDRESS */ PEAssembly; // Actually the module address in .NET 9+ - public ulong /* CLRDATA_ADDRESS */ LoadedPEAddress; + public ClrDataAddress PEAssembly; // Actually the module address in .NET 9+ + public ClrDataAddress LoadedPEAddress; public ulong LoadedPESize; - public ulong /* CLRDATA_ADDRESS */ InMemoryPdbAddress; + public ClrDataAddress InMemoryPdbAddress; public ulong InMemoryPdbSize; } @@ -169,16 +169,16 @@ internal unsafe partial interface IXCLRDataProcess int SetDesiredExecutionState(uint state); [PreserveSig] - int GetAddressType(ulong address, /*CLRDataAddressType*/ uint* type); + int GetAddressType(ClrDataAddress address, /*CLRDataAddressType*/ uint* type); [PreserveSig] int GetRuntimeNameByAddress( - ulong address, + ClrDataAddress address, uint flags, uint bufLen, uint* nameLen, char* nameBuf, - ulong* displacement); + ClrDataAddress* displacement); [PreserveSig] int StartEnumAppDomains(ulong* handle); @@ -203,7 +203,7 @@ int GetRuntimeNameByAddress( [PreserveSig] int EndEnumModules(ulong handle); [PreserveSig] - int GetModuleByAddress(ulong address, /*IXCLRDataModule*/ void** mod); + int GetModuleByAddress(ClrDataAddress address, /*IXCLRDataModule*/ void** mod); [PreserveSig] int StartEnumMethodInstancesByAddress(ulong address, /*IXCLRDataAppDomain*/ void* appDomain, ulong* handle); @@ -214,7 +214,7 @@ int GetRuntimeNameByAddress( [PreserveSig] int GetDataByAddress( - ulong address, + ClrDataAddress address, uint flags, /*IXCLRDataAppDomain*/ void* appDomain, /*IXCLRDataTask*/ void* tlsTask, @@ -222,7 +222,7 @@ int GetDataByAddress( uint* nameLen, char* nameBuf, /*IXCLRDataValue*/ void** value, - ulong* displacement); + ClrDataAddress* displacement); [PreserveSig] int GetExceptionStateByExceptionRecord(/*struct EXCEPTION_RECORD64*/ void* record, /*IXCLRDataExceptionState*/ void** exState); @@ -237,7 +237,7 @@ int CreateMemoryValue( /*IXCLRDataAppDomain*/ void* appDomain, /*IXCLRDataTask*/ void* tlsTask, /*IXCLRDataTypeInstance*/ void* type, - ulong addr, + ClrDataAddress addr, /*IXCLRDataValue*/ void** value); [PreserveSig] @@ -280,7 +280,7 @@ int SetCodeNotifications( int SetOtherNotificationFlags(uint flags); [PreserveSig] - int StartEnumMethodDefinitionsByAddress(ulong address, ulong* handle); + int StartEnumMethodDefinitionsByAddress(ClrDataAddress address, ulong* handle); [PreserveSig] int EnumMethodDefinitionByAddress(ulong* handle, /*IXCLRDataMethodDefinition*/ void** method); [PreserveSig] @@ -289,24 +289,24 @@ int SetCodeNotifications( [PreserveSig] int FollowStub( uint inFlags, - ulong inAddr, + ClrDataAddress inAddr, /*struct CLRDATA_FOLLOW_STUB_BUFFER*/ void* inBuffer, - ulong* outAddr, + ClrDataAddress* outAddr, /*struct CLRDATA_FOLLOW_STUB_BUFFER*/ void* outBuffer, uint* outFlags); [PreserveSig] int FollowStub2( /*IXCLRDataTask*/ void* task, uint inFlags, - ulong inAddr, + ClrDataAddress inAddr, /*struct CLRDATA_FOLLOW_STUB_BUFFER*/ void* inBuffer, - ulong* outAddr, + ClrDataAddress* outAddr, /*struct CLRDATA_FOLLOW_STUB_BUFFER*/ void* outBuffer, uint* outFlags); [PreserveSig] int DumpNativeImage( - ulong loadedBase, + ClrDataAddress loadedBase, char* name, /*IXCLRDataDisplay*/ void* display, /*IXCLRLibrarySupport*/ void* libSupport, diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.IXCLRDataProcess.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.IXCLRDataProcess.cs index f04d39aa8000a1..8ec6452c6d7cf3 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.IXCLRDataProcess.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.IXCLRDataProcess.cs @@ -86,16 +86,16 @@ int IXCLRDataProcess.GetDesiredExecutionState(uint* state) int IXCLRDataProcess.SetDesiredExecutionState(uint state) => _legacyProcess is not null ? _legacyProcess.SetDesiredExecutionState(state) : HResults.E_NOTIMPL; - int IXCLRDataProcess.GetAddressType(ulong address, /*CLRDataAddressType*/ uint* type) + int IXCLRDataProcess.GetAddressType(ClrDataAddress address, /*CLRDataAddressType*/ uint* type) => _legacyProcess is not null ? _legacyProcess.GetAddressType(address, type) : HResults.E_NOTIMPL; int IXCLRDataProcess.GetRuntimeNameByAddress( - ulong address, + ClrDataAddress address, uint flags, uint bufLen, uint* nameLen, char* nameBuf, - ulong* displacement) + ClrDataAddress* displacement) => _legacyProcess is not null ? _legacyProcess.GetRuntimeNameByAddress(address, flags, bufLen, nameLen, nameBuf, displacement) : HResults.E_NOTIMPL; int IXCLRDataProcess.StartEnumAppDomains(ulong* handle) @@ -128,7 +128,7 @@ int IXCLRDataProcess.EnumModule(ulong* handle, /*IXCLRDataModule*/ void** mod) int IXCLRDataProcess.EndEnumModules(ulong handle) => _legacyProcess is not null ? _legacyProcess.EndEnumModules(handle) : HResults.E_NOTIMPL; - int IXCLRDataProcess.GetModuleByAddress(ulong address, /*IXCLRDataModule*/ void** mod) + int IXCLRDataProcess.GetModuleByAddress(ClrDataAddress address, /*IXCLRDataModule*/ void** mod) => _legacyProcess is not null ? _legacyProcess.GetModuleByAddress(address, mod) : HResults.E_NOTIMPL; int IXCLRDataProcess.StartEnumMethodInstancesByAddress(ulong address, /*IXCLRDataAppDomain*/ void* appDomain, ulong* handle) @@ -141,7 +141,7 @@ int IXCLRDataProcess.EndEnumMethodInstancesByAddress(ulong handle) => _legacyProcess is not null ? _legacyProcess.EndEnumMethodInstancesByAddress(handle) : HResults.E_NOTIMPL; int IXCLRDataProcess.GetDataByAddress( - ulong address, + ClrDataAddress address, uint flags, /*IXCLRDataAppDomain*/ void* appDomain, /*IXCLRDataTask*/ void* tlsTask, @@ -149,7 +149,7 @@ int IXCLRDataProcess.GetDataByAddress( uint* nameLen, char* nameBuf, /*IXCLRDataValue*/ void** value, - ulong* displacement) + ClrDataAddress* displacement) => _legacyProcess is not null ? _legacyProcess.GetDataByAddress(address, flags, appDomain, tlsTask, bufLen, nameLen, nameBuf, value, displacement) : HResults.E_NOTIMPL; int IXCLRDataProcess.GetExceptionStateByExceptionRecord(/*struct EXCEPTION_RECORD64*/ void* record, /*IXCLRDataExceptionState*/ void** exState) @@ -165,7 +165,7 @@ int IXCLRDataProcess.CreateMemoryValue( /*IXCLRDataAppDomain*/ void* appDomain, /*IXCLRDataTask*/ void* tlsTask, /*IXCLRDataTypeInstance*/ void* type, - ulong addr, + ClrDataAddress addr, /*IXCLRDataValue*/ void** value) => _legacyProcess is not null ? _legacyProcess.CreateMemoryValue(appDomain, tlsTask, type, addr, value) : HResults.E_NOTIMPL; @@ -215,7 +215,7 @@ int IXCLRDataProcess.GetOtherNotificationFlags(uint* flags) int IXCLRDataProcess.SetOtherNotificationFlags(uint flags) => _legacyProcess is not null ? _legacyProcess.SetOtherNotificationFlags(flags) : HResults.E_NOTIMPL; - int IXCLRDataProcess.StartEnumMethodDefinitionsByAddress(ulong address, ulong* handle) + int IXCLRDataProcess.StartEnumMethodDefinitionsByAddress(ClrDataAddress address, ulong* handle) => _legacyProcess is not null ? _legacyProcess.StartEnumMethodDefinitionsByAddress(address, handle) : HResults.E_NOTIMPL; int IXCLRDataProcess.EnumMethodDefinitionByAddress(ulong* handle, /*IXCLRDataMethodDefinition*/ void** method) @@ -226,9 +226,9 @@ int IXCLRDataProcess.EndEnumMethodDefinitionsByAddress(ulong handle) int IXCLRDataProcess.FollowStub( uint inFlags, - ulong inAddr, + ClrDataAddress inAddr, /*struct CLRDATA_FOLLOW_STUB_BUFFER*/ void* inBuffer, - ulong* outAddr, + ClrDataAddress* outAddr, /*struct CLRDATA_FOLLOW_STUB_BUFFER*/ void* outBuffer, uint* outFlags) => _legacyProcess is not null ? _legacyProcess.FollowStub(inFlags, inAddr, inBuffer, outAddr, outBuffer, outFlags) : HResults.E_NOTIMPL; @@ -236,15 +236,15 @@ int IXCLRDataProcess.FollowStub( int IXCLRDataProcess.FollowStub2( /*IXCLRDataTask*/ void* task, uint inFlags, - ulong inAddr, + ClrDataAddress inAddr, /*struct CLRDATA_FOLLOW_STUB_BUFFER*/ void* inBuffer, - ulong* outAddr, + ClrDataAddress* outAddr, /*struct CLRDATA_FOLLOW_STUB_BUFFER*/ void* outBuffer, uint* outFlags) => _legacyProcess is not null ? _legacyProcess.FollowStub2(task, inFlags, inAddr, inBuffer, outAddr, outBuffer, outFlags) : HResults.E_NOTIMPL; int IXCLRDataProcess.DumpNativeImage( - ulong loadedBase, + ClrDataAddress loadedBase, char* name, /*IXCLRDataDisplay*/ void* display, /*IXCLRLibrarySupport*/ void* libSupport, diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 7c36d2cd100b11..66355c5b466b51 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -92,7 +92,7 @@ public SOSDacImpl(Target target, object? legacyObj) } #region ISOSDacInterface - int ISOSDacInterface.GetAppDomainConfigFile(ulong appDomain, int count, char* configFile, uint* pNeeded) + int ISOSDacInterface.GetAppDomainConfigFile(ClrDataAddress appDomain, int count, char* configFile, uint* pNeeded) { // Method is not supported on CoreCLR int hr = HResults.E_FAIL; @@ -107,15 +107,15 @@ int ISOSDacInterface.GetAppDomainConfigFile(ulong appDomain, int count, char* co return hr; } - int ISOSDacInterface.GetAppDomainData(ulong addr, void* data) + int ISOSDacInterface.GetAppDomainData(ClrDataAddress addr, void* data) => _legacyImpl is not null ? _legacyImpl.GetAppDomainData(addr, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetAppDomainList(uint count, [In, MarshalUsing(CountElementName = "count"), Out] ulong[] values, uint* pNeeded) + int ISOSDacInterface.GetAppDomainList(uint count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[] values, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetAppDomainList(count, values, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetAppDomainName(ulong addr, uint count, char* name, uint* pNeeded) + int ISOSDacInterface.GetAppDomainName(ClrDataAddress addr, uint count, char* name, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetAppDomainName(addr, count, name, pNeeded) : HResults.E_NOTIMPL; int ISOSDacInterface.GetAppDomainStoreData(void* data) => _legacyImpl is not null ? _legacyImpl.GetAppDomainStoreData(data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetApplicationBase(ulong appDomain, int count, char* appBase, uint* pNeeded) + int ISOSDacInterface.GetApplicationBase(ClrDataAddress appDomain, int count, char* appBase, uint* pNeeded) { // Method is not supported on CoreCLR int hr = HResults.E_FAIL; @@ -130,11 +130,11 @@ int ISOSDacInterface.GetApplicationBase(ulong appDomain, int count, char* appBas return hr; } - int ISOSDacInterface.GetAssemblyData(ulong baseDomainPtr, ulong assembly, void* data) + int ISOSDacInterface.GetAssemblyData(ClrDataAddress baseDomainPtr, ClrDataAddress assembly, void* data) => _legacyImpl is not null ? _legacyImpl.GetAssemblyData(baseDomainPtr, assembly, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetAssemblyList(ulong appDomain, int count, [In, MarshalUsing(CountElementName = "count"), Out] ulong[]? values, int* pNeeded) + int ISOSDacInterface.GetAssemblyList(ClrDataAddress addr, int count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[]? values, int* pNeeded) { - if (appDomain == 0) + if (addr == 0) { return HResults.E_INVALIDARG; } @@ -143,9 +143,10 @@ int ISOSDacInterface.GetAssemblyList(ulong appDomain, int count, [In, MarshalUsi try { + TargetPointer appDomain = addr.ToTargetPointer(_target); TargetPointer systemDomainPtr = _target.ReadGlobalPointer(Constants.Globals.SystemDomain); - TargetPointer systemDomain = _target.ReadPointer(systemDomainPtr); - if (appDomain == systemDomain) + ClrDataAddress systemDomain = _target.ReadPointer(systemDomainPtr).ToClrDataAddress(_target); + if (addr == systemDomain) { // We shouldn't be asking for the assemblies in SystemDomain hr = HResults.E_INVALIDARG; @@ -167,7 +168,7 @@ int ISOSDacInterface.GetAssemblyList(ulong appDomain, int count, [In, MarshalUsi Contracts.ModuleHandle module = modules[i]; if (loader.IsAssemblyLoaded(module)) { - values[n++] = loader.GetAssembly(module); + values[n++] = loader.GetAssembly(module).ToClrDataAddress(_target); } } } @@ -197,9 +198,9 @@ int ISOSDacInterface.GetAssemblyList(ulong appDomain, int count, [In, MarshalUsi #if DEBUG if (_legacyImpl is not null) { - ulong[]? valuesLocal = values != null ? new ulong[count] : null; + ClrDataAddress[]? valuesLocal = values != null ? new ClrDataAddress[count] : null; int neededLocal; - int hrLocal = _legacyImpl.GetAssemblyList(appDomain, count, valuesLocal, &neededLocal); + int hrLocal = _legacyImpl.GetAssemblyList(addr, count, valuesLocal, &neededLocal); Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); if (hr == HResults.S_OK) { @@ -219,27 +220,27 @@ int ISOSDacInterface.GetAssemblyList(ulong appDomain, int count, [In, MarshalUsi return hr; } - int ISOSDacInterface.GetAssemblyLocation(ulong assembly, int count, char* location, uint* pNeeded) + int ISOSDacInterface.GetAssemblyLocation(ClrDataAddress assembly, int count, char* location, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetAssemblyLocation(assembly, count, location, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetAssemblyModuleList(ulong assembly, uint count, [In, MarshalUsing(CountElementName = "count"), Out] ulong[] modules, uint* pNeeded) + int ISOSDacInterface.GetAssemblyModuleList(ClrDataAddress assembly, uint count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[] modules, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetAssemblyModuleList(assembly, count, modules, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetAssemblyName(ulong assembly, uint count, char* name, uint* pNeeded) + int ISOSDacInterface.GetAssemblyName(ClrDataAddress assembly, uint count, char* name, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetAssemblyName(assembly, count, name, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetCCWData(ulong ccw, void* data) + int ISOSDacInterface.GetCCWData(ClrDataAddress ccw, void* data) => _legacyImpl is not null ? _legacyImpl.GetCCWData(ccw, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetCCWInterfaces(ulong ccw, uint count, void* interfaces, uint* pNeeded) + int ISOSDacInterface.GetCCWInterfaces(ClrDataAddress ccw, uint count, void* interfaces, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetCCWInterfaces(ccw, count, interfaces, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetClrWatsonBuckets(ulong thread, void* pGenericModeBlock) + int ISOSDacInterface.GetClrWatsonBuckets(ClrDataAddress thread, void* pGenericModeBlock) => _legacyImpl is not null ? _legacyImpl.GetClrWatsonBuckets(thread, pGenericModeBlock) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetCodeHeaderData(ulong ip, void* data) + int ISOSDacInterface.GetCodeHeaderData(ClrDataAddress ip, void* data) => _legacyImpl is not null ? _legacyImpl.GetCodeHeaderData(ip, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetCodeHeapList(ulong jitManager, uint count, void* codeHeaps, uint* pNeeded) + int ISOSDacInterface.GetCodeHeapList(ClrDataAddress jitManager, uint count, void* codeHeaps, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetCodeHeapList(jitManager, count, codeHeaps, pNeeded) : HResults.E_NOTIMPL; int ISOSDacInterface.GetDacModuleHandle(void* phModule) => _legacyImpl is not null ? _legacyImpl.GetDacModuleHandle(phModule) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetDomainFromContext(ulong context, ulong* domain) + int ISOSDacInterface.GetDomainFromContext(ClrDataAddress context, ClrDataAddress* domain) => _legacyImpl is not null ? _legacyImpl.GetDomainFromContext(context, domain) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetDomainLocalModuleData(ulong addr, void* data) + int ISOSDacInterface.GetDomainLocalModuleData(ClrDataAddress addr, void* data) { // CoreCLR does not use domain local modules anymore int hr = HResults.E_NOTIMPL; @@ -255,7 +256,7 @@ int ISOSDacInterface.GetDomainLocalModuleData(ulong addr, void* data) return hr; } - int ISOSDacInterface.GetDomainLocalModuleDataFromAppDomain(ulong appDomainAddr, int moduleID, void* data) + int ISOSDacInterface.GetDomainLocalModuleDataFromAppDomain(ClrDataAddress appDomainAddr, int moduleID, void* data) { // CoreCLR does not support multi-appdomain shared assembly loading. Thus, a non-pointer sized moduleID cannot exist. int hr = HResults.E_INVALIDARG; @@ -270,7 +271,7 @@ int ISOSDacInterface.GetDomainLocalModuleDataFromAppDomain(ulong appDomainAddr, return hr; } - int ISOSDacInterface.GetDomainLocalModuleDataFromModule(ulong moduleAddr, void* data) + int ISOSDacInterface.GetDomainLocalModuleDataFromModule(ClrDataAddress moduleAddr, void* data) { // CoreCLR does not use domain local modules anymore int hr = HResults.E_NOTIMPL; @@ -285,17 +286,17 @@ int ISOSDacInterface.GetDomainLocalModuleDataFromModule(ulong moduleAddr, void* return hr; } - int ISOSDacInterface.GetFailedAssemblyData(ulong assembly, uint* pContext, int* pResult) + int ISOSDacInterface.GetFailedAssemblyData(ClrDataAddress assembly, uint* pContext, int* pResult) => _legacyImpl is not null ? _legacyImpl.GetFailedAssemblyData(assembly, pContext, pResult) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetFailedAssemblyDisplayName(ulong assembly, uint count, char* name, uint* pNeeded) + int ISOSDacInterface.GetFailedAssemblyDisplayName(ClrDataAddress assembly, uint count, char* name, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetFailedAssemblyDisplayName(assembly, count, name, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetFailedAssemblyList(ulong appDomain, int count, [In, MarshalUsing(CountElementName = "count"), Out] ulong[] values, uint* pNeeded) + int ISOSDacInterface.GetFailedAssemblyList(ClrDataAddress appDomain, int count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[] values, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetFailedAssemblyList(appDomain, count, values, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetFailedAssemblyLocation(ulong assesmbly, uint count, char* location, uint* pNeeded) + int ISOSDacInterface.GetFailedAssemblyLocation(ClrDataAddress assesmbly, uint count, char* location, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetFailedAssemblyLocation(assesmbly, count, location, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetFieldDescData(ulong fieldDesc, void* data) + int ISOSDacInterface.GetFieldDescData(ClrDataAddress fieldDesc, void* data) => _legacyImpl is not null ? _legacyImpl.GetFieldDescData(fieldDesc, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetFrameName(ulong vtable, uint count, char* frameName, uint* pNeeded) + int ISOSDacInterface.GetFrameName(ClrDataAddress vtable, uint count, char* frameName, uint* pNeeded) { if (vtable == 0) { @@ -353,9 +354,9 @@ int ISOSDacInterface.GetFrameName(ulong vtable, uint count, char* frameName, uin } int ISOSDacInterface.GetGCHeapData(void* data) => _legacyImpl is not null ? _legacyImpl.GetGCHeapData(data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetGCHeapDetails(ulong heap, void* details) + int ISOSDacInterface.GetGCHeapDetails(ClrDataAddress heap, void* details) => _legacyImpl is not null ? _legacyImpl.GetGCHeapDetails(heap, details) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetGCHeapList(uint count, [In, MarshalUsing(CountElementName = "count"), Out] ulong[] heaps, uint* pNeeded) + int ISOSDacInterface.GetGCHeapList(uint count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[] heaps, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetGCHeapList(count, heaps, pNeeded) : HResults.E_NOTIMPL; int ISOSDacInterface.GetGCHeapStaticData(void* data) => _legacyImpl is not null ? _legacyImpl.GetGCHeapStaticData(data) : HResults.E_NOTIMPL; @@ -367,14 +368,14 @@ int ISOSDacInterface.GetHandleEnumForTypes([In, MarshalUsing(CountElementName = => _legacyImpl is not null ? _legacyImpl.GetHandleEnumForTypes(types, count, ppHandleEnum) : HResults.E_NOTIMPL; int ISOSDacInterface.GetHeapAllocData(uint count, void* data, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetHeapAllocData(count, data, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetHeapAnalyzeData(ulong addr, void* data) + int ISOSDacInterface.GetHeapAnalyzeData(ClrDataAddress addr, void* data) => _legacyImpl is not null ? _legacyImpl.GetHeapAnalyzeData(addr, data) : HResults.E_NOTIMPL; int ISOSDacInterface.GetHeapAnalyzeStaticData(void* data) => _legacyImpl is not null ? _legacyImpl.GetHeapAnalyzeStaticData(data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetHeapSegmentData(ulong seg, void* data) + int ISOSDacInterface.GetHeapSegmentData(ClrDataAddress seg, void* data) => _legacyImpl is not null ? _legacyImpl.GetHeapSegmentData(seg, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetHillClimbingLogEntry(ulong addr, void* data) + int ISOSDacInterface.GetHillClimbingLogEntry(ClrDataAddress addr, void* data) { // This API is not implemented by the legacy DAC int hr = HResults.E_NOTIMPL; @@ -389,17 +390,17 @@ int ISOSDacInterface.GetHillClimbingLogEntry(ulong addr, void* data) return hr; } - int ISOSDacInterface.GetILForModule(ulong moduleAddr, int rva, ulong* il) + int ISOSDacInterface.GetILForModule(ClrDataAddress moduleAddr, int rva, ClrDataAddress* il) => _legacyImpl is not null ? _legacyImpl.GetILForModule(moduleAddr, rva, il) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetJitHelperFunctionName(ulong ip, uint count, byte* name, uint* pNeeded) + int ISOSDacInterface.GetJitHelperFunctionName(ClrDataAddress ip, uint count, byte* name, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetJitHelperFunctionName(ip, count, name, pNeeded) : HResults.E_NOTIMPL; int ISOSDacInterface.GetJitManagerList(uint count, void* managers, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetJitManagerList(count, managers, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetJumpThunkTarget(void* ctx, ulong* targetIP, ulong* targetMD) + int ISOSDacInterface.GetJumpThunkTarget(void* ctx, ClrDataAddress* targetIP, ClrDataAddress* targetMD) => _legacyImpl is not null ? _legacyImpl.GetJumpThunkTarget(ctx, targetIP, targetMD) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDescData* data, uint cRevertedRejitVersions, DacpReJitData* rgRevertedRejitData, uint* pcNeededRevertedRejitData) + int ISOSDacInterface.GetMethodDescData(ClrDataAddress addr, ClrDataAddress ip, DacpMethodDescData* data, uint cRevertedRejitVersions, DacpReJitData* rgRevertedRejitData, uint* pcNeededRevertedRejitData) { - if (methodDesc == 0) + if (addr == 0) { return HResults.E_INVALIDARG; } @@ -418,6 +419,8 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes try { Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem; + + TargetPointer methodDesc = addr.ToTargetPointer(_target); Contracts.MethodDescHandle methodDescHandle = rtsContract.GetMethodDescHandle(methodDesc); Contracts.ICodeVersions nativeCodeContract = _target.Contracts.CodeVersions; Contracts.IReJIT rejitContract = _target.Contracts.ReJIT; @@ -454,7 +457,7 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes if (nativeCodeAddr != TargetCodePointer.Null) { data->bHasNativeCode = 1; - data->NativeCodeAddr = nativeCodeAddr; + data->NativeCodeAddr = nativeCodeAddr.AsTargetPointer.ToClrDataAddress(_target); } else { @@ -463,18 +466,18 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes } if (rtsContract.HasNativeCodeSlot(methodDescHandle)) { - data->AddressOfNativeCodeSlot = rtsContract.GetAddressOfNativeCodeSlot(methodDescHandle); + data->AddressOfNativeCodeSlot = rtsContract.GetAddressOfNativeCodeSlot(methodDescHandle).ToClrDataAddress(_target); } else { data->AddressOfNativeCodeSlot = 0; } data->MDToken = rtsContract.GetMethodToken(methodDescHandle); - data->MethodDescPtr = methodDesc; + data->MethodDescPtr = addr; TargetPointer methodTableAddr = rtsContract.GetMethodTable(methodDescHandle); - data->MethodTablePtr = methodTableAddr; + data->MethodTablePtr = methodTableAddr.ToClrDataAddress(_target); TypeHandle typeHandle = rtsContract.GetTypeHandle(methodTableAddr); - data->ModulePtr = rtsContract.GetModule(typeHandle); + data->ModulePtr = rtsContract.GetModule(typeHandle).ToClrDataAddress(_target); // If rejit info is appropriate, get the following: // * ReJitInfo for the current, active version of the method @@ -575,7 +578,7 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes // TargetPointer.Null if GCCover information is not available. // In certain minidumps, we won't save the GCCover information. // (it would be unwise to do so, it is heavy and not a customer scenario). - data->GCStressCodeCopy = nativeCodeContract.GetGCStressCodeCopy(requestedNativeCodeVersion); + data->GCStressCodeCopy = nativeCodeContract.GetGCStressCodeCopy(requestedNativeCodeVersion).ToClrDataAddress(_target); } // Unlike the legacy implementation, the cDAC does not currently populate @@ -607,7 +610,7 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes int hrLocal; fixed (DacpReJitData* rgRevertedRejitDataLocalPtr = rgRevertedRejitDataLocal) { - hrLocal = _legacyImpl.GetMethodDescData(methodDesc, ip, &dataLocal, cRevertedRejitVersions, rgRevertedRejitDataLocalPtr, pcNeededRevertedRejitDataLocal); + hrLocal = _legacyImpl.GetMethodDescData(addr, ip, &dataLocal, cRevertedRejitVersions, rgRevertedRejitDataLocalPtr, pcNeededRevertedRejitDataLocal); } Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); if (hr == HResults.S_OK) @@ -666,7 +669,7 @@ private void CopyNativeCodeVersionToReJitData( ILCodeVersionHandle ilCodeVersion = cv.GetILCodeVersion(nativeCodeVersion); pReJitData->rejitID = rejit.GetRejitId(ilCodeVersion).Value; - pReJitData->NativeCodeAddr = cv.GetNativeCode(nativeCodeVersion); + pReJitData->NativeCodeAddr = cv.GetNativeCode(nativeCodeVersion).Value; if (nativeCodeVersion.CodeVersionNodeAddress != activeNativeCodeVersion.CodeVersionNodeAddress || nativeCodeVersion.MethodDescAddress != activeNativeCodeVersion.MethodDescAddress) @@ -694,11 +697,11 @@ private void CopyNativeCodeVersionToReJitData( } } - int ISOSDacInterface.GetMethodDescFromToken(ulong moduleAddr, uint token, ulong* methodDesc) + int ISOSDacInterface.GetMethodDescFromToken(ClrDataAddress moduleAddr, uint token, ClrDataAddress* methodDesc) => _legacyImpl is not null ? _legacyImpl.GetMethodDescFromToken(moduleAddr, token, methodDesc) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetMethodDescName(ulong methodDesc, uint count, char* name, uint* pNeeded) + int ISOSDacInterface.GetMethodDescName(ClrDataAddress addr, uint count, char* name, uint* pNeeded) { - if (methodDesc == 0) + if (addr == 0) return HResults.E_INVALIDARG; int hr = HResults.S_OK; @@ -706,6 +709,7 @@ int ISOSDacInterface.GetMethodDescName(ulong methodDesc, uint count, char* name, *pNeeded = 0; try { + TargetPointer methodDesc = addr.ToTargetPointer(_target); StringBuilder stringBuilder = new StringBuilder(); Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem; Contracts.MethodDescHandle methodDescHandle = rtsContract.GetMethodDescHandle(methodDesc); @@ -754,7 +758,7 @@ int ISOSDacInterface.GetMethodDescName(ulong methodDesc, uint count, char* name, } } - if (hr == HResults.S_OK) + if (hr == HResults.S_OK) { OutputBufferHelpers.CopyStringToBuffer(name, count, pNeeded, stringBuilder.ToString()); } @@ -772,7 +776,7 @@ int ISOSDacInterface.GetMethodDescName(ulong methodDesc, uint count, char* name, int hrLocal; fixed (char* ptr = nameLocal) { - hrLocal = _legacyImpl.GetMethodDescName(methodDesc, count, ptr, &neededLocal); + hrLocal = _legacyImpl.GetMethodDescName(addr, count, ptr, &neededLocal); } Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); if (hr == HResults.S_OK) @@ -785,9 +789,9 @@ int ISOSDacInterface.GetMethodDescName(ulong methodDesc, uint count, char* name, return hr; } - int ISOSDacInterface.GetMethodDescPtrFromFrame(ulong frameAddr, ulong* ppMD) + int ISOSDacInterface.GetMethodDescPtrFromFrame(ClrDataAddress frameAddr, ClrDataAddress* ppMD) => _legacyImpl is not null ? _legacyImpl.GetMethodDescPtrFromFrame(frameAddr, ppMD) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetMethodDescPtrFromIP(ulong ip, ulong* ppMD) + int ISOSDacInterface.GetMethodDescPtrFromIP(ClrDataAddress ip, ClrDataAddress* ppMD) { if (ip == 0 || ppMD == null) return HResults.E_INVALIDARG; @@ -831,7 +835,7 @@ int ISOSDacInterface.GetMethodDescPtrFromIP(ulong ip, ulong* ppMD) #if DEBUG if (_legacyImpl is not null) { - ulong ppMDLocal; + ClrDataAddress ppMDLocal; int hrLocal = _legacyImpl.GetMethodDescPtrFromIP(ip, &ppMDLocal); Debug.Assert(hrLocal == hr); @@ -844,9 +848,9 @@ int ISOSDacInterface.GetMethodDescPtrFromIP(ulong ip, ulong* ppMD) return hr; } - int ISOSDacInterface.GetMethodDescTransparencyData(ulong methodDesc, void* data) + int ISOSDacInterface.GetMethodDescTransparencyData(ClrDataAddress methodDesc, void* data) => _legacyImpl is not null ? _legacyImpl.GetMethodDescTransparencyData(methodDesc, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetMethodTableData(ulong mt, DacpMethodTableData* data) + int ISOSDacInterface.GetMethodTableData(ClrDataAddress mt, DacpMethodTableData* data) { if (mt == 0 || data == null) return HResults.E_INVALIDARG; @@ -855,7 +859,7 @@ int ISOSDacInterface.GetMethodTableData(ulong mt, DacpMethodTableData* data) try { Contracts.IRuntimeTypeSystem contract = _target.Contracts.RuntimeTypeSystem; - Contracts.TypeHandle methodTable = contract.GetTypeHandle(mt); + Contracts.TypeHandle methodTable = contract.GetTypeHandle(mt.ToTargetPointer(_target)); DacpMethodTableData result = default; result.baseSize = contract.GetBaseSize(methodTable); @@ -872,10 +876,10 @@ int ISOSDacInterface.GetMethodTableData(ulong mt, DacpMethodTableData* data) result.bIsFree = isFreeObjectMT ? 1 : 0; if (!isFreeObjectMT) { - result.module = contract.GetModule(methodTable); + result.module = contract.GetModule(methodTable).ToClrDataAddress(_target); // Note: really the canonical method table, not the EEClass, which we don't expose - result.klass = contract.GetCanonicalMethodTable(methodTable); - result.parentMethodTable = contract.GetParentMethodTable(methodTable); + result.klass = contract.GetCanonicalMethodTable(methodTable).ToClrDataAddress(_target); + result.parentMethodTable = contract.GetParentMethodTable(methodTable).ToClrDataAddress(_target); result.wNumInterfaces = contract.GetNumInterfaces(methodTable); result.wNumMethods = contract.GetNumMethods(methodTable); result.wNumVtableSlots = 0; // always return 0 since .NET 9 @@ -918,9 +922,9 @@ int ISOSDacInterface.GetMethodTableData(ulong mt, DacpMethodTableData* data) #endif return hr; } - int ISOSDacInterface.GetMethodTableFieldData(ulong mt, void* data) + int ISOSDacInterface.GetMethodTableFieldData(ClrDataAddress mt, void* data) => _legacyImpl is not null ? _legacyImpl.GetMethodTableFieldData(mt, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetMethodTableForEEClass(ulong eeClassReallyCanonMT, ulong* value) + int ISOSDacInterface.GetMethodTableForEEClass(ClrDataAddress eeClassReallyCanonMT, ClrDataAddress* value) { if (eeClassReallyCanonMT == 0 || value == null) return HResults.E_INVALIDARG; @@ -929,8 +933,8 @@ int ISOSDacInterface.GetMethodTableForEEClass(ulong eeClassReallyCanonMT, ulong* try { Contracts.IRuntimeTypeSystem contract = _target.Contracts.RuntimeTypeSystem; - Contracts.TypeHandle methodTableHandle = contract.GetTypeHandle(eeClassReallyCanonMT); - *value = methodTableHandle.Address; + Contracts.TypeHandle methodTableHandle = contract.GetTypeHandle(eeClassReallyCanonMT.ToTargetPointer(_target)); + *value = methodTableHandle.Address.ToClrDataAddress(_target); } catch (global::System.Exception ex) { @@ -940,7 +944,7 @@ int ISOSDacInterface.GetMethodTableForEEClass(ulong eeClassReallyCanonMT, ulong* #if DEBUG if (_legacyImpl is not null) { - ulong valueLocal; + ClrDataAddress valueLocal; int hrLocal = _legacyImpl.GetMethodTableForEEClass(eeClassReallyCanonMT, &valueLocal); Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); if (hr == HResults.S_OK) @@ -950,7 +954,7 @@ int ISOSDacInterface.GetMethodTableForEEClass(ulong eeClassReallyCanonMT, ulong* return hr; } - int ISOSDacInterface.GetMethodTableName(ulong mt, uint count, char* mtName, uint* pNeeded) + int ISOSDacInterface.GetMethodTableName(ClrDataAddress mt, uint count, char* mtName, uint* pNeeded) { if (mt == 0) return HResults.E_INVALIDARG; @@ -959,7 +963,7 @@ int ISOSDacInterface.GetMethodTableName(ulong mt, uint count, char* mtName, uint try { Contracts.IRuntimeTypeSystem typeSystemContract = _target.Contracts.RuntimeTypeSystem; - Contracts.TypeHandle methodTableHandle = typeSystemContract.GetTypeHandle(mt); + Contracts.TypeHandle methodTableHandle = typeSystemContract.GetTypeHandle(mt.ToTargetPointer(_target)); if (typeSystemContract.IsFreeObjectMethodTable(methodTableHandle)) { OutputBufferHelpers.CopyStringToBuffer(mtName, count, pNeeded, "Free"); @@ -978,7 +982,7 @@ int ISOSDacInterface.GetMethodTableName(ulong mt, uint count, char* mtName, uint { try { - string? fallbackName = _target.Contracts.DacStreams.StringFromEEAddress(mt); + string? fallbackName = _target.Contracts.DacStreams.StringFromEEAddress(mt.ToTargetPointer(_target)); if (fallbackName != null) { methodTableName.Clear(); @@ -1016,11 +1020,11 @@ int ISOSDacInterface.GetMethodTableName(ulong mt, uint count, char* mtName, uint return hr; } - int ISOSDacInterface.GetMethodTableSlot(ulong mt, uint slot, ulong* value) + int ISOSDacInterface.GetMethodTableSlot(ClrDataAddress mt, uint slot, ClrDataAddress* value) => _legacyImpl is not null ? _legacyImpl.GetMethodTableSlot(mt, slot, value) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetMethodTableTransparencyData(ulong mt, void* data) + int ISOSDacInterface.GetMethodTableTransparencyData(ClrDataAddress mt, void* data) => _legacyImpl is not null ? _legacyImpl.GetMethodTableTransparencyData(mt, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetModule(ulong addr, out IXCLRDataModule? mod) + int ISOSDacInterface.GetModule(ClrDataAddress addr, out IXCLRDataModule? mod) { mod = default; @@ -1032,11 +1036,11 @@ int ISOSDacInterface.GetModule(ulong addr, out IXCLRDataModule? mod) return hr; } - mod = new ClrDataModule(addr, _target, legacyModule); + mod = new ClrDataModule(addr.ToTargetPointer(_target), _target, legacyModule); return HResults.S_OK; } - int ISOSDacInterface.GetModuleData(ulong moduleAddr, DacpModuleData* data) + int ISOSDacInterface.GetModuleData(ClrDataAddress moduleAddr, DacpModuleData* data) { if (moduleAddr == 0 || data == null) return HResults.E_INVALIDARG; @@ -1045,11 +1049,11 @@ int ISOSDacInterface.GetModuleData(ulong moduleAddr, DacpModuleData* data) try { Contracts.ILoader contract = _target.Contracts.Loader; - Contracts.ModuleHandle handle = contract.GetModuleHandle(moduleAddr); + Contracts.ModuleHandle handle = contract.GetModuleHandle(moduleAddr.ToTargetPointer(_target)); data->Address = moduleAddr; data->PEAssembly = moduleAddr; // Module address in .NET 9+ - correspondingly, SOS-DAC APIs for PE assemblies expect a module address - data->Assembly = contract.GetAssembly(handle); + data->Assembly = contract.GetAssembly(handle).ToClrDataAddress(_target); Contracts.ModuleFlags flags = contract.GetFlags(handle); bool isReflectionEmit = flags.HasFlag(Contracts.ModuleFlags.ReflectionEmit); @@ -1057,12 +1061,12 @@ int ISOSDacInterface.GetModuleData(ulong moduleAddr, DacpModuleData* data) data->isPEFile = (uint)(isReflectionEmit ? 0 : 1); // ReflectionEmit module means it is not a PE file data->dwTransientFlags = (uint)flags; - data->ilBase = contract.GetILBase(handle); + data->ilBase = contract.GetILBase(handle).ToClrDataAddress(_target); try { TargetSpan readOnlyMetadata = _target.Contracts.EcmaMetadata.GetReadOnlyMetadataAddress(handle); - data->metadataStart = readOnlyMetadata.Address; + data->metadataStart = readOnlyMetadata.Address.Value; data->metadataSize = readOnlyMetadata.Size; } catch (System.Exception) @@ -1073,18 +1077,18 @@ int ISOSDacInterface.GetModuleData(ulong moduleAddr, DacpModuleData* data) data->metadataSize = 0; } - data->LoaderAllocator = contract.GetLoaderAllocator(handle); + data->LoaderAllocator = contract.GetLoaderAllocator(handle).ToClrDataAddress(_target); Target.TypeInfo lookupMapTypeInfo = _target.GetTypeInfo(DataType.ModuleLookupMap); ulong tableDataOffset = (ulong)lookupMapTypeInfo.Fields[Constants.FieldNames.ModuleLookupMap.TableData].Offset; Contracts.ModuleLookupTables tables = contract.GetLookupTables(handle); - data->FieldDefToDescMap = _target.ReadPointer(tables.FieldDefToDesc + tableDataOffset); - data->ManifestModuleReferencesMap = _target.ReadPointer(tables.ManifestModuleReferences + tableDataOffset); - data->MemberRefToDescMap = _target.ReadPointer(tables.MemberRefToDesc + tableDataOffset); - data->MethodDefToDescMap = _target.ReadPointer(tables.MethodDefToDesc + tableDataOffset); - data->TypeDefToMethodTableMap = _target.ReadPointer(tables.TypeDefToMethodTable + tableDataOffset); - data->TypeRefToMethodTableMap = _target.ReadPointer(tables.TypeRefToMethodTable + tableDataOffset); + data->FieldDefToDescMap = _target.ReadPointer(tables.FieldDefToDesc + tableDataOffset).ToClrDataAddress(_target); + data->ManifestModuleReferencesMap = _target.ReadPointer(tables.ManifestModuleReferences + tableDataOffset).ToClrDataAddress(_target); + data->MemberRefToDescMap = _target.ReadPointer(tables.MemberRefToDesc + tableDataOffset).ToClrDataAddress(_target); + data->MethodDefToDescMap = _target.ReadPointer(tables.MethodDefToDesc + tableDataOffset).ToClrDataAddress(_target); + data->TypeDefToMethodTableMap = _target.ReadPointer(tables.TypeDefToMethodTable + tableDataOffset).ToClrDataAddress(_target); + data->TypeRefToMethodTableMap = _target.ReadPointer(tables.TypeRefToMethodTable + tableDataOffset).ToClrDataAddress(_target); // Always 0 - .NET no longer has these concepts data->dwModuleID = 0; @@ -1131,7 +1135,7 @@ int ISOSDacInterface.GetModuleData(ulong moduleAddr, DacpModuleData* data) return hr; } - int ISOSDacInterface.GetNestedExceptionData(ulong exception, ulong* exceptionObject, ulong* nextNestedException) + int ISOSDacInterface.GetNestedExceptionData(ClrDataAddress exception, ClrDataAddress* exceptionObject, ClrDataAddress* nextNestedException) { if (exception == 0 || exceptionObject == null || nextNestedException == null) return HResults.E_INVALIDARG; @@ -1140,9 +1144,11 @@ int ISOSDacInterface.GetNestedExceptionData(ulong exception, ulong* exceptionObj try { Contracts.IException contract = _target.Contracts.Exception; - TargetPointer exceptionObjectLocal = contract.GetNestedExceptionInfo(exception, out TargetPointer nextNestedExceptionLocal); - *exceptionObject = exceptionObjectLocal; - *nextNestedException = nextNestedExceptionLocal; + TargetPointer exceptionObjectLocal = contract.GetNestedExceptionInfo( + exception.ToTargetPointer(_target), + out TargetPointer nextNestedExceptionLocal); + *exceptionObject = exceptionObjectLocal.ToClrDataAddress(_target); + *nextNestedException = nextNestedExceptionLocal.Value; } catch (global::System.Exception ex) { @@ -1152,8 +1158,8 @@ int ISOSDacInterface.GetNestedExceptionData(ulong exception, ulong* exceptionObj #if DEBUG if (_legacyImpl is not null) { - ulong exceptionObjectLocal; - ulong nextNestedExceptionLocal; + ClrDataAddress exceptionObjectLocal; + ClrDataAddress nextNestedExceptionLocal; int hrLocal = _legacyImpl.GetNestedExceptionData(exception, &exceptionObjectLocal, &nextNestedExceptionLocal); Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); if (hr == HResults.S_OK) @@ -1166,10 +1172,10 @@ int ISOSDacInterface.GetNestedExceptionData(ulong exception, ulong* exceptionObj return hr; } - int ISOSDacInterface.GetObjectClassName(ulong obj, uint count, char* className, uint* pNeeded) + int ISOSDacInterface.GetObjectClassName(ClrDataAddress obj, uint count, char* className, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetObjectClassName(obj, count, className, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetObjectData(ulong objAddr, DacpObjectData* data) + int ISOSDacInterface.GetObjectData(ClrDataAddress objAddr, DacpObjectData* data) { if (objAddr == 0 || data == null) return HResults.E_INVALIDARG; @@ -1180,10 +1186,11 @@ int ISOSDacInterface.GetObjectData(ulong objAddr, DacpObjectData* data) Contracts.IObject objectContract = _target.Contracts.Object; Contracts.IRuntimeTypeSystem runtimeTypeSystemContract = _target.Contracts.RuntimeTypeSystem; - TargetPointer mt = objectContract.GetMethodTableAddress(objAddr); + TargetPointer objPtr = objAddr.ToTargetPointer(_target); + TargetPointer mt = objectContract.GetMethodTableAddress(objPtr); TypeHandle handle = runtimeTypeSystemContract.GetTypeHandle(mt); - data->MethodTable = mt; + data->MethodTable = mt.ToClrDataAddress(_target); data->Size = runtimeTypeSystemContract.GetBaseSize(handle); data->dwComponentSize = runtimeTypeSystemContract.GetComponentSize(handle); @@ -1202,7 +1209,7 @@ int ISOSDacInterface.GetObjectData(ulong objAddr, DacpObjectData* data) data->ObjectType = DacpObjectType.OBJ_STRING; // Update the size to include the string character components - data->Size += (uint)objectContract.GetStringValue(objAddr).Length * data->dwComponentSize; + data->Size += (uint)objectContract.GetStringValue(objPtr).Length * data->dwComponentSize; } else if (mt == _objectMethodTable.Value) { @@ -1213,18 +1220,18 @@ int ISOSDacInterface.GetObjectData(ulong objAddr, DacpObjectData* data) data->ObjectType = DacpObjectType.OBJ_ARRAY; data->dwRank = rank; - TargetPointer arrayData = objectContract.GetArrayData(objAddr, out uint numComponents, out TargetPointer boundsStart, out TargetPointer lowerBounds); - data->ArrayDataPtr = arrayData; + TargetPointer arrayData = objectContract.GetArrayData(objPtr, out uint numComponents, out TargetPointer boundsStart, out TargetPointer lowerBounds); + data->ArrayDataPtr = arrayData.ToClrDataAddress(_target); data->dwNumComponents = numComponents; - data->ArrayBoundsPtr = boundsStart; - data->ArrayLowerBoundsPtr = lowerBounds; + data->ArrayBoundsPtr = boundsStart.ToClrDataAddress(_target); + data->ArrayLowerBoundsPtr = lowerBounds.ToClrDataAddress(_target); // Update the size to include the array components data->Size += numComponents * data->dwComponentSize; // Get the type of the array elements TypeHandle element = runtimeTypeSystemContract.GetTypeParam(handle); - data->ElementTypeHandle = element.Address; + data->ElementTypeHandle = element.Address.Value; data->ElementType = (uint)runtimeTypeSystemContract.GetSignatureCorElementType(element); // Validate the element type handles for arrays of arrays @@ -1240,7 +1247,7 @@ int ISOSDacInterface.GetObjectData(ulong objAddr, DacpObjectData* data) // Populate COM data if this is a COM object if (_target.ReadGlobal(Constants.Globals.FeatureCOMInterop) != 0 - && objectContract.GetBuiltInComData(objAddr, out TargetPointer rcw, out TargetPointer ccw)) + && objectContract.GetBuiltInComData(objPtr, out TargetPointer rcw, out TargetPointer ccw)) { data->RCW = rcw; data->CCW = ccw; @@ -1279,7 +1286,7 @@ int ISOSDacInterface.GetObjectData(ulong objAddr, DacpObjectData* data) return hr; } - int ISOSDacInterface.GetObjectStringData(ulong obj, uint count, char* stringData, uint* pNeeded) + int ISOSDacInterface.GetObjectStringData(ClrDataAddress obj, uint count, char* stringData, uint* pNeeded) { if (obj == 0 || (stringData == null && pNeeded == null) || (stringData is not null && count <= 0)) return HResults.E_INVALIDARG; @@ -1288,7 +1295,7 @@ int ISOSDacInterface.GetObjectStringData(ulong obj, uint count, char* stringData try { Contracts.IObject contract = _target.Contracts.Object; - string str = contract.GetStringValue(obj); + string str = contract.GetStringValue(obj.ToTargetPointer(_target)); OutputBufferHelpers.CopyStringToBuffer(stringData, count, pNeeded, str); } catch (System.Exception ex) @@ -1318,12 +1325,12 @@ int ISOSDacInterface.GetObjectStringData(ulong obj, uint count, char* stringData return hr; } - int ISOSDacInterface.GetOOMData(ulong oomAddr, void* data) + int ISOSDacInterface.GetOOMData(ClrDataAddress oomAddr, void* data) => _legacyImpl is not null ? _legacyImpl.GetOOMData(oomAddr, data) : HResults.E_NOTIMPL; int ISOSDacInterface.GetOOMStaticData(void* data) => _legacyImpl is not null ? _legacyImpl.GetOOMStaticData(data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetPEFileBase(ulong addr, ulong* peBase) + int ISOSDacInterface.GetPEFileBase(ClrDataAddress addr, ClrDataAddress* peBase) { if (addr == 0 || peBase == null) return HResults.E_INVALIDARG; @@ -1332,12 +1339,12 @@ int ISOSDacInterface.GetPEFileBase(ulong addr, ulong* peBase) try { Contracts.ILoader contract = _target.Contracts.Loader; - Contracts.ModuleHandle handle = contract.GetModuleHandle(addr); + Contracts.ModuleHandle handle = contract.GetModuleHandle(addr.ToTargetPointer(_target)); Contracts.ModuleFlags flags = contract.GetFlags(handle); if (!flags.HasFlag(Contracts.ModuleFlags.ReflectionEmit)) { - *peBase = contract.GetILBase(handle); + *peBase = contract.GetILBase(handle).ToClrDataAddress(_target); } else { @@ -1352,7 +1359,7 @@ int ISOSDacInterface.GetPEFileBase(ulong addr, ulong* peBase) #if DEBUG if (_legacyImpl is not null) { - ulong peBaseLocal; + ClrDataAddress peBaseLocal; int hrLocal = _legacyImpl.GetPEFileBase(addr, &peBaseLocal); Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); if (hr == HResults.S_OK) @@ -1362,7 +1369,7 @@ int ISOSDacInterface.GetPEFileBase(ulong addr, ulong* peBase) return hr; } - int ISOSDacInterface.GetPEFileName(ulong addr, uint count, char* fileName, uint* pNeeded) + int ISOSDacInterface.GetPEFileName(ClrDataAddress addr, uint count, char* fileName, uint* pNeeded) { if (addr == 0 || (fileName == null && pNeeded == null) || (fileName is not null && count <= 0)) return HResults.E_INVALIDARG; @@ -1371,7 +1378,7 @@ int ISOSDacInterface.GetPEFileName(ulong addr, uint count, char* fileName, uint* try { Contracts.ILoader contract = _target.Contracts.Loader; - Contracts.ModuleHandle handle = contract.GetModuleHandle(addr); + Contracts.ModuleHandle handle = contract.GetModuleHandle(addr.ToTargetPointer(_target)); string path = contract.GetPath(handle); // Return not implemented for empty paths for non-reflection emit assemblies (for example, loaded from memory) @@ -1412,7 +1419,7 @@ int ISOSDacInterface.GetPEFileName(ulong addr, uint count, char* fileName, uint* return hr; } - int ISOSDacInterface.GetPrivateBinPaths(ulong appDomain, int count, char* paths, uint* pNeeded) + int ISOSDacInterface.GetPrivateBinPaths(ClrDataAddress appDomain, int count, char* paths, uint* pNeeded) { // Method is not supported on CoreCLR int hr = HResults.E_NOTIMPL; @@ -1427,25 +1434,25 @@ int ISOSDacInterface.GetPrivateBinPaths(ulong appDomain, int count, char* paths, return hr; } - int ISOSDacInterface.GetRCWData(ulong addr, void* data) + int ISOSDacInterface.GetRCWData(ClrDataAddress addr, void* data) => _legacyImpl is not null ? _legacyImpl.GetRCWData(addr, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetRCWInterfaces(ulong rcw, uint count, void* interfaces, uint* pNeeded) + int ISOSDacInterface.GetRCWInterfaces(ClrDataAddress rcw, uint count, void* interfaces, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetRCWInterfaces(rcw, count, interfaces, pNeeded) : HResults.E_NOTIMPL; int ISOSDacInterface.GetRegisterName(int regName, uint count, char* buffer, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetRegisterName(regName, count, buffer, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetStackLimits(ulong threadPtr, ulong* lower, ulong* upper, ulong* fp) + int ISOSDacInterface.GetStackLimits(ClrDataAddress threadPtr, ClrDataAddress* lower, ClrDataAddress* upper, ClrDataAddress* fp) => _legacyImpl is not null ? _legacyImpl.GetStackLimits(threadPtr, lower, upper, fp) : HResults.E_NOTIMPL; int ISOSDacInterface.GetStackReferences(int osThreadID, void** ppEnum) => _legacyImpl is not null ? _legacyImpl.GetStackReferences(osThreadID, ppEnum) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetStressLogAddress(ulong* stressLog) + int ISOSDacInterface.GetStressLogAddress(ClrDataAddress* stressLog) { ulong stressLogAddress = _target.ReadGlobalPointer(Constants.Globals.StressLog); #if DEBUG if (_legacyImpl is not null) { - ulong legacyStressLog; + ClrDataAddress legacyStressLog; Debug.Assert(HResults.S_OK == _legacyImpl.GetStressLogAddress(&legacyStressLog)); Debug.Assert(legacyStressLog == stressLogAddress); } @@ -1454,14 +1461,14 @@ int ISOSDacInterface.GetStressLogAddress(ulong* stressLog) return HResults.S_OK; } - int ISOSDacInterface.GetSyncBlockCleanupData(ulong addr, void* data) + int ISOSDacInterface.GetSyncBlockCleanupData(ClrDataAddress addr, void* data) => _legacyImpl is not null ? _legacyImpl.GetSyncBlockCleanupData(addr, data) : HResults.E_NOTIMPL; int ISOSDacInterface.GetSyncBlockData(uint number, void* data) => _legacyImpl is not null ? _legacyImpl.GetSyncBlockData(number, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetThreadAllocData(ulong thread, void* data) + int ISOSDacInterface.GetThreadAllocData(ClrDataAddress thread, void* data) => _legacyImpl is not null ? _legacyImpl.GetThreadAllocData(thread, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetThreadData(ulong thread, DacpThreadData* data) + int ISOSDacInterface.GetThreadData(ClrDataAddress thread, DacpThreadData* data) { if (thread == 0 || data == null) return HResults.E_INVALIDARG; @@ -1470,26 +1477,26 @@ int ISOSDacInterface.GetThreadData(ulong thread, DacpThreadData* data) try { Contracts.IThread contract = _target.Contracts.Thread; - Contracts.ThreadData threadData = contract.GetThreadData(thread); + Contracts.ThreadData threadData = contract.GetThreadData(thread.ToTargetPointer(_target)); data->corThreadId = (int)threadData.Id; data->osThreadId = (int)threadData.OSId.Value; data->state = (int)threadData.State; data->preemptiveGCDisabled = (uint)(threadData.PreemptiveGCDisabled ? 1 : 0); - data->allocContextPtr = threadData.AllocContextPointer; - data->allocContextLimit = threadData.AllocContextLimit; + data->allocContextPtr = threadData.AllocContextPointer.ToClrDataAddress(_target); + data->allocContextLimit = threadData.AllocContextLimit.ToClrDataAddress(_target); data->fiberData = 0; // Always set to 0 - fibers are no longer supported TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain); TargetPointer appDomain = _target.ReadPointer(appDomainPointer); - data->context = appDomain; - data->domain = appDomain; + data->context = appDomain.ToClrDataAddress(_target); + data->domain = appDomain.ToClrDataAddress(_target); data->lockCount = -1; // Always set to -1 - lock count was .NET Framework and no longer needed - data->pFrame = threadData.Frame; - data->firstNestedException = threadData.FirstNestedException; - data->teb = threadData.TEB; - data->lastThrownObjectHandle = threadData.LastThrownObjectHandle; - data->nextThread = threadData.NextThread; + data->pFrame = threadData.Frame.ToClrDataAddress(_target); + data->firstNestedException = threadData.FirstNestedException.ToClrDataAddress(_target); + data->teb = threadData.TEB.ToClrDataAddress(_target); + data->lastThrownObjectHandle = threadData.LastThrownObjectHandle.ToClrDataAddress(_target); + data->nextThread = threadData.NextThread.ToClrDataAddress(_target); } catch (global::System.Exception ex) { @@ -1524,9 +1531,9 @@ int ISOSDacInterface.GetThreadData(ulong thread, DacpThreadData* data) #endif return hr; } - int ISOSDacInterface.GetThreadFromThinlockID(uint thinLockId, ulong* pThread) + int ISOSDacInterface.GetThreadFromThinlockID(uint thinLockId, ClrDataAddress* pThread) => _legacyImpl is not null ? _legacyImpl.GetThreadFromThinlockID(thinLockId, pThread) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetThreadLocalModuleData(ulong thread, uint index, void* data) + int ISOSDacInterface.GetThreadLocalModuleData(ClrDataAddress thread, uint index, void* data) { // CoreCLR does not use thread local modules anymore int hr = HResults.E_NOTIMPL; @@ -1569,9 +1576,9 @@ int ISOSDacInterface.GetThreadStoreData(DacpThreadStoreData* data) Contracts.IThread thread = _target.Contracts.Thread; Contracts.ThreadStoreData threadStoreData = thread.GetThreadStoreData(); data->threadCount = threadStoreData.ThreadCount; - data->firstThread = threadStoreData.FirstThread; - data->finalizerThread = threadStoreData.FinalizerThread; - data->gcThread = threadStoreData.GCThread; + data->firstThread = threadStoreData.FirstThread.ToClrDataAddress(_target); + data->finalizerThread = threadStoreData.FinalizerThread.ToClrDataAddress(_target); + data->gcThread = threadStoreData.GCThread.ToClrDataAddress(_target); Contracts.ThreadStoreCounts threadCounts = thread.GetThreadCounts(); data->unstartedThreadCount = threadCounts.UnstartedThreadCount; @@ -1621,15 +1628,20 @@ int ISOSDacInterface.GetUsefulGlobals(DacpUsefulGlobalsData* data) try { data->ArrayMethodTable = _target.ReadPointer( - _target.ReadGlobalPointer(Constants.Globals.ObjectArrayMethodTable)); + _target.ReadGlobalPointer(Constants.Globals.ObjectArrayMethodTable)) + .ToClrDataAddress(_target); data->StringMethodTable = _target.ReadPointer( - _target.ReadGlobalPointer(Constants.Globals.StringMethodTable)); + _target.ReadGlobalPointer(Constants.Globals.StringMethodTable)) + .ToClrDataAddress(_target); data->ObjectMethodTable = _target.ReadPointer( - _target.ReadGlobalPointer(Constants.Globals.ObjectMethodTable)); + _target.ReadGlobalPointer(Constants.Globals.ObjectMethodTable)) + .ToClrDataAddress(_target); data->ExceptionMethodTable = _target.ReadPointer( - _target.ReadGlobalPointer(Constants.Globals.ExceptionMethodTable)); + _target.ReadGlobalPointer(Constants.Globals.ExceptionMethodTable)) + .ToClrDataAddress(_target); data->FreeMethodTable = _target.ReadPointer( - _target.ReadGlobalPointer(Constants.Globals.FreeObjectMethodTable)); + _target.ReadGlobalPointer(Constants.Globals.FreeObjectMethodTable)) + .ToClrDataAddress(_target); } catch (System.Exception ex) { @@ -1664,7 +1676,7 @@ int ISOSDacInterface.GetUsefulGlobals(DacpUsefulGlobalsData* data) return hr; } - int ISOSDacInterface.GetWorkRequestData(ulong addrWorkRequest, void* data) + int ISOSDacInterface.GetWorkRequestData(ClrDataAddress addrWorkRequest, void* data) { // This API is not implemented by the legacy DAC int hr = HResults.E_NOTIMPL; @@ -1679,31 +1691,31 @@ int ISOSDacInterface.GetWorkRequestData(ulong addrWorkRequest, void* data) return hr; } - int ISOSDacInterface.TraverseEHInfo(ulong ip, void* pCallback, void* token) + int ISOSDacInterface.TraverseEHInfo(ClrDataAddress ip, void* pCallback, void* token) => _legacyImpl is not null ? _legacyImpl.TraverseEHInfo(ip, pCallback, token) : HResults.E_NOTIMPL; - int ISOSDacInterface.TraverseLoaderHeap(ulong loaderHeapAddr, void* pCallback) + int ISOSDacInterface.TraverseLoaderHeap(ClrDataAddress loaderHeapAddr, void* pCallback) => _legacyImpl is not null ? _legacyImpl.TraverseLoaderHeap(loaderHeapAddr, pCallback) : HResults.E_NOTIMPL; - int ISOSDacInterface.TraverseModuleMap(int mmt, ulong moduleAddr, void* pCallback, void* token) + int ISOSDacInterface.TraverseModuleMap(int mmt, ClrDataAddress moduleAddr, void* pCallback, void* token) => _legacyImpl is not null ? _legacyImpl.TraverseModuleMap(mmt, moduleAddr, pCallback, token) : HResults.E_NOTIMPL; - int ISOSDacInterface.TraverseRCWCleanupList(ulong cleanupListPtr, void* pCallback, void* token) + int ISOSDacInterface.TraverseRCWCleanupList(ClrDataAddress cleanupListPtr, void* pCallback, void* token) => _legacyImpl is not null ? _legacyImpl.TraverseRCWCleanupList(cleanupListPtr, pCallback, token) : HResults.E_NOTIMPL; - int ISOSDacInterface.TraverseVirtCallStubHeap(ulong pAppDomain, int heaptype, void* pCallback) + int ISOSDacInterface.TraverseVirtCallStubHeap(ClrDataAddress pAppDomain, int heaptype, void* pCallback) => _legacyImpl is not null ? _legacyImpl.TraverseVirtCallStubHeap(pAppDomain, heaptype, pCallback) : HResults.E_NOTIMPL; #endregion ISOSDacInterface #region ISOSDacInterface2 - int ISOSDacInterface2.GetObjectExceptionData(ulong objectAddress, DacpExceptionObjectData* data) + int ISOSDacInterface2.GetObjectExceptionData(ClrDataAddress objectAddress, DacpExceptionObjectData* data) { try { Contracts.IException contract = _target.Contracts.Exception; - Contracts.ExceptionData exceptionData = contract.GetExceptionData(objectAddress); - data->Message = exceptionData.Message; - data->InnerException = exceptionData.InnerException; - data->StackTrace = exceptionData.StackTrace; - data->WatsonBuckets = exceptionData.WatsonBuckets; - data->StackTraceString = exceptionData.StackTraceString; - data->RemoteStackTraceString = exceptionData.RemoteStackTraceString; + Contracts.ExceptionData exceptionData = contract.GetExceptionData(objectAddress.ToTargetPointer(_target)); + data->Message = exceptionData.Message.ToClrDataAddress(_target); + data->InnerException = exceptionData.InnerException.ToClrDataAddress(_target); + data->StackTrace = exceptionData.StackTrace.ToClrDataAddress(_target); + data->WatsonBuckets = exceptionData.WatsonBuckets.ToClrDataAddress(_target); + data->StackTraceString = exceptionData.StackTraceString.ToClrDataAddress(_target); + data->RemoteStackTraceString = exceptionData.RemoteStackTraceString.ToClrDataAddress(_target); data->HResult = exceptionData.HResult; data->XCode = exceptionData.XCode; } @@ -1715,12 +1727,12 @@ int ISOSDacInterface2.GetObjectExceptionData(ulong objectAddress, DacpExceptionO return HResults.S_OK; } - int ISOSDacInterface2.IsRCWDCOMProxy(ulong rcwAddress, int* inDCOMProxy) + int ISOSDacInterface2.IsRCWDCOMProxy(ClrDataAddress rcwAddress, int* inDCOMProxy) => _legacyImpl2 is not null ? _legacyImpl2.IsRCWDCOMProxy(rcwAddress, inDCOMProxy) : HResults.E_NOTIMPL; #endregion ISOSDacInterface2 #region ISOSDacInterface3 - int ISOSDacInterface3.GetGCInterestingInfoData(ulong interestingInfoAddr, /*struct DacpGCInterestingInfoData*/ void* data) + int ISOSDacInterface3.GetGCInterestingInfoData(ClrDataAddress interestingInfoAddr, /*struct DacpGCInterestingInfoData*/ void* data) => _legacyImpl3 is not null ? _legacyImpl3.GetGCInterestingInfoData(interestingInfoAddr, data) : HResults.E_NOTIMPL; int ISOSDacInterface3.GetGCInterestingInfoStaticData(/*struct DacpGCInterestingInfoData*/ void* data) => _legacyImpl3 is not null ? _legacyImpl3.GetGCInterestingInfoStaticData(data) : HResults.E_NOTIMPL; @@ -1729,28 +1741,28 @@ int ISOSDacInterface3.GetGCGlobalMechanisms(nuint* globalMechanisms) #endregion ISOSDacInterface3 #region ISOSDacInterface4 - int ISOSDacInterface4.GetClrNotification(ulong[] arguments, int count, int* pNeeded) + int ISOSDacInterface4.GetClrNotification(ClrDataAddress[] arguments, int count, int* pNeeded) => _legacyImpl4 is not null ? _legacyImpl4.GetClrNotification(arguments, count, pNeeded) : HResults.E_NOTIMPL; #endregion ISOSDacInterface4 #region ISOSDacInterface5 - int ISOSDacInterface5.GetTieredVersions(ulong methodDesc, int rejitId, /*struct DacpTieredVersionData*/ void* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs) + int ISOSDacInterface5.GetTieredVersions(ClrDataAddress methodDesc, int rejitId, /*struct DacpTieredVersionData*/ void* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs) => _legacyImpl5 is not null ? _legacyImpl5.GetTieredVersions(methodDesc, rejitId, nativeCodeAddrs, cNativeCodeAddrs, pcNativeCodeAddrs) : HResults.E_NOTIMPL; #endregion ISOSDacInterface5 #region ISOSDacInterface6 - int ISOSDacInterface6.GetMethodTableCollectibleData(ulong mt, /*struct DacpMethodTableCollectibleData*/ void* data) + int ISOSDacInterface6.GetMethodTableCollectibleData(ClrDataAddress mt, /*struct DacpMethodTableCollectibleData*/ void* data) => _legacyImpl6 is not null ? _legacyImpl6.GetMethodTableCollectibleData(mt, data) : HResults.E_NOTIMPL; #endregion ISOSDacInterface6 #region ISOSDacInterface7 - int ISOSDacInterface7.GetPendingReJITID(ulong methodDesc, int* pRejitId) + int ISOSDacInterface7.GetPendingReJITID(ClrDataAddress methodDesc, int* pRejitId) => _legacyImpl7 is not null ? _legacyImpl7.GetPendingReJITID(methodDesc, pRejitId) : HResults.E_NOTIMPL; - int ISOSDacInterface7.GetReJITInformation(ulong methodDesc, int rejitId, /*struct DacpReJitData2*/ void* pRejitData) + int ISOSDacInterface7.GetReJITInformation(ClrDataAddress methodDesc, int rejitId, /*struct DacpReJitData2*/ void* pRejitData) => _legacyImpl7 is not null ? _legacyImpl7.GetReJITInformation(methodDesc, rejitId, pRejitData) : HResults.E_NOTIMPL; - int ISOSDacInterface7.GetProfilerModifiedILInformation(ulong methodDesc, /*struct DacpProfilerILData*/ void* pILData) + int ISOSDacInterface7.GetProfilerModifiedILInformation(ClrDataAddress methodDesc, /*struct DacpProfilerILData*/ void* pILData) => _legacyImpl7 is not null ? _legacyImpl7.GetProfilerModifiedILInformation(methodDesc, pILData) : HResults.E_NOTIMPL; - int ISOSDacInterface7.GetMethodsWithProfilerModifiedIL(ulong mod, ulong* methodDescs, int cMethodDescs, int* pcMethodDescs) + int ISOSDacInterface7.GetMethodsWithProfilerModifiedIL(ClrDataAddress mod, ClrDataAddress* methodDescs, int cMethodDescs, int* pcMethodDescs) => _legacyImpl7 is not null ? _legacyImpl7.GetMethodsWithProfilerModifiedIL(mod, methodDescs, cMethodDescs, pcMethodDescs) : HResults.E_NOTIMPL; #endregion ISOSDacInterface7 @@ -1761,16 +1773,16 @@ int ISOSDacInterface8.GetNumberGenerations(uint* pGenerations) // WKS int ISOSDacInterface8.GetGenerationTable(uint cGenerations, /*struct DacpGenerationData*/ void* pGenerationData, uint* pNeeded) => _legacyImpl8 is not null ? _legacyImpl8.GetGenerationTable(cGenerations, pGenerationData, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface8.GetFinalizationFillPointers(uint cFillPointers, ulong* pFinalizationFillPointers, uint* pNeeded) + int ISOSDacInterface8.GetFinalizationFillPointers(uint cFillPointers, ClrDataAddress* pFinalizationFillPointers, uint* pNeeded) => _legacyImpl8 is not null ? _legacyImpl8.GetFinalizationFillPointers(cFillPointers, pFinalizationFillPointers, pNeeded) : HResults.E_NOTIMPL; // SVR - int ISOSDacInterface8.GetGenerationTableSvr(ulong heapAddr, uint cGenerations, /*struct DacpGenerationData*/ void* pGenerationData, uint* pNeeded) + int ISOSDacInterface8.GetGenerationTableSvr(ClrDataAddress heapAddr, uint cGenerations, /*struct DacpGenerationData*/ void* pGenerationData, uint* pNeeded) => _legacyImpl8 is not null ? _legacyImpl8.GetGenerationTableSvr(heapAddr, cGenerations, pGenerationData, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface8.GetFinalizationFillPointersSvr(ulong heapAddr, uint cFillPointers, ulong* pFinalizationFillPointers, uint* pNeeded) + int ISOSDacInterface8.GetFinalizationFillPointersSvr(ClrDataAddress heapAddr, uint cFillPointers, ClrDataAddress* pFinalizationFillPointers, uint* pNeeded) => _legacyImpl8 is not null ? _legacyImpl8.GetFinalizationFillPointersSvr(heapAddr, cFillPointers, pFinalizationFillPointers, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface8.GetAssemblyLoadContext(ulong methodTable, ulong* assemblyLoadContext) + int ISOSDacInterface8.GetAssemblyLoadContext(ClrDataAddress methodTable, ClrDataAddress* assemblyLoadContext) => _legacyImpl8 is not null ? _legacyImpl8.GetAssemblyLoadContext(methodTable, assemblyLoadContext) : HResults.E_NOTIMPL; #endregion ISOSDacInterface8 @@ -1790,38 +1802,38 @@ int ISOSDacInterface9.GetBreakingChangeVersion() #endregion ISOSDacInterface9 #region ISOSDacInterface10 - int ISOSDacInterface10.GetObjectComWrappersData(ulong objAddr, ulong* rcw, uint count, ulong* mowList, uint* pNeeded) + int ISOSDacInterface10.GetObjectComWrappersData(ClrDataAddress objAddr, ClrDataAddress* rcw, uint count, ClrDataAddress* mowList, uint* pNeeded) => _legacyImpl10 is not null ? _legacyImpl10.GetObjectComWrappersData(objAddr, rcw, count, mowList, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface10.IsComWrappersCCW(ulong ccw, Interop.BOOL* isComWrappersCCW) + int ISOSDacInterface10.IsComWrappersCCW(ClrDataAddress ccw, Interop.BOOL* isComWrappersCCW) => _legacyImpl10 is not null ? _legacyImpl10.IsComWrappersCCW(ccw, isComWrappersCCW) : HResults.E_NOTIMPL; - int ISOSDacInterface10.GetComWrappersCCWData(ulong ccw, ulong* managedObject, int* refCount) + int ISOSDacInterface10.GetComWrappersCCWData(ClrDataAddress ccw, ClrDataAddress* managedObject, int* refCount) => _legacyImpl10 is not null ? _legacyImpl10.GetComWrappersCCWData(ccw, managedObject, refCount) : HResults.E_NOTIMPL; - int ISOSDacInterface10.IsComWrappersRCW(ulong rcw, Interop.BOOL* isComWrappersRCW) + int ISOSDacInterface10.IsComWrappersRCW(ClrDataAddress rcw, Interop.BOOL* isComWrappersRCW) => _legacyImpl10 is not null ? _legacyImpl10.IsComWrappersRCW(rcw, isComWrappersRCW) : HResults.E_NOTIMPL; - int ISOSDacInterface10.GetComWrappersRCWData(ulong rcw, ulong* identity) + int ISOSDacInterface10.GetComWrappersRCWData(ClrDataAddress rcw, ClrDataAddress* identity) => _legacyImpl10 is not null ? _legacyImpl10.GetComWrappersRCWData(rcw, identity) : HResults.E_NOTIMPL; #endregion ISOSDacInterface10 #region ISOSDacInterface11 - int ISOSDacInterface11.IsTrackedType(ulong objAddr, Interop.BOOL* isTrackedType, Interop.BOOL* hasTaggedMemory) + int ISOSDacInterface11.IsTrackedType(ClrDataAddress objAddr, Interop.BOOL* isTrackedType, Interop.BOOL* hasTaggedMemory) => _legacyImpl11 is not null ? _legacyImpl11.IsTrackedType(objAddr, isTrackedType, hasTaggedMemory) : HResults.E_NOTIMPL; - int ISOSDacInterface11.GetTaggedMemory(ulong objAddr, ulong* taggedMemory, nuint* taggedMemorySizeInBytes) + int ISOSDacInterface11.GetTaggedMemory(ClrDataAddress objAddr, ClrDataAddress* taggedMemory, nuint* taggedMemorySizeInBytes) => _legacyImpl11 is not null ? _legacyImpl11.GetTaggedMemory(objAddr, taggedMemory, taggedMemorySizeInBytes) : HResults.E_NOTIMPL; #endregion ISOSDacInterface11 #region ISOSDacInterface12 - int ISOSDacInterface12.GetGlobalAllocationContext(ulong* allocPtr, ulong* allocLimit) + int ISOSDacInterface12.GetGlobalAllocationContext(ClrDataAddress* allocPtr, ClrDataAddress* allocLimit) => _legacyImpl12 is not null ? _legacyImpl12.GetGlobalAllocationContext(allocPtr, allocLimit) : HResults.E_NOTIMPL; #endregion ISOSDacInterface12 #region ISOSDacInterface13 - int ISOSDacInterface13.TraverseLoaderHeap(ulong loaderHeapAddr, /*LoaderHeapKind*/ int kind, /*VISITHEAP*/ delegate* unmanaged pCallback) + int ISOSDacInterface13.TraverseLoaderHeap(ClrDataAddress loaderHeapAddr, /*LoaderHeapKind*/ int kind, /*VISITHEAP*/ delegate* unmanaged pCallback) => _legacyImpl13 is not null ? _legacyImpl13.TraverseLoaderHeap(loaderHeapAddr, kind, pCallback) : HResults.E_NOTIMPL; - int ISOSDacInterface13.GetDomainLoaderAllocator(ulong domainAddress, ulong* pLoaderAllocator) + int ISOSDacInterface13.GetDomainLoaderAllocator(ClrDataAddress domainAddress, ClrDataAddress* pLoaderAllocator) => _legacyImpl13 is not null ? _legacyImpl13.GetDomainLoaderAllocator(domainAddress, pLoaderAllocator) : HResults.E_NOTIMPL; int ISOSDacInterface13.GetLoaderAllocatorHeapNames(int count, char** ppNames, int* pNeeded) => _legacyImpl13 is not null ? _legacyImpl13.GetLoaderAllocatorHeapNames(count, ppNames, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface13.GetLoaderAllocatorHeaps(ulong loaderAllocator, int count, ulong* pLoaderHeaps, /*LoaderHeapKind*/ int* pKinds, int* pNeeded) + int ISOSDacInterface13.GetLoaderAllocatorHeaps(ClrDataAddress loaderAllocator, int count, ClrDataAddress* pLoaderHeaps, /*LoaderHeapKind*/ int* pKinds, int* pNeeded) => _legacyImpl13 is not null ? _legacyImpl13.GetLoaderAllocatorHeaps(loaderAllocator, count, pLoaderHeaps, pKinds, pNeeded) : HResults.E_NOTIMPL; int ISOSDacInterface13.GetHandleTableMemoryRegions(/*ISOSMemoryEnum*/ void** ppEnum) => _legacyImpl13 is not null ? _legacyImpl13.GetHandleTableMemoryRegions(ppEnum) : HResults.E_NOTIMPL; @@ -1834,16 +1846,16 @@ int ISOSDacInterface13.LockedFlush() #endregion ISOSDacInterface13 #region ISOSDacInterface14 - int ISOSDacInterface14.GetStaticBaseAddress(ulong methodTable, ulong* nonGCStaticsAddress, ulong* GCStaticsAddress) + int ISOSDacInterface14.GetStaticBaseAddress(ClrDataAddress methodTable, ClrDataAddress* nonGCStaticsAddress, ClrDataAddress* GCStaticsAddress) => _legacyImpl14 is not null ? _legacyImpl14.GetStaticBaseAddress(methodTable, nonGCStaticsAddress, GCStaticsAddress) : HResults.E_NOTIMPL; - int ISOSDacInterface14.GetThreadStaticBaseAddress(ulong methodTable, ulong thread, ulong* nonGCStaticsAddress, ulong* GCStaticsAddress) + int ISOSDacInterface14.GetThreadStaticBaseAddress(ClrDataAddress methodTable, ClrDataAddress thread, ClrDataAddress* nonGCStaticsAddress, ClrDataAddress* GCStaticsAddress) => _legacyImpl14 is not null ? _legacyImpl14.GetThreadStaticBaseAddress(methodTable, thread, nonGCStaticsAddress, GCStaticsAddress) : HResults.E_NOTIMPL; - int ISOSDacInterface14.GetMethodTableInitializationFlags(ulong methodTable, /*MethodTableInitializationFlags*/ int* initializationStatus) + int ISOSDacInterface14.GetMethodTableInitializationFlags(ClrDataAddress methodTable, /*MethodTableInitializationFlags*/ int* initializationStatus) => _legacyImpl14 is not null ? _legacyImpl14.GetMethodTableInitializationFlags(methodTable, initializationStatus) : HResults.E_NOTIMPL; #endregion ISOSDacInterface14 #region ISOSDacInterface15 - int ISOSDacInterface15.GetMethodTableSlotEnumerator(ulong mt, /*ISOSMethodEnum*/void** enumerator) + int ISOSDacInterface15.GetMethodTableSlotEnumerator(ClrDataAddress mt, /*ISOSMethodEnum*/void** enumerator) => _legacyImpl15 is not null ? _legacyImpl15.GetMethodTableSlotEnumerator(mt, enumerator) : HResults.E_NOTIMPL; #endregion ISOSDacInterface15 } From 7909b7d4aab66d7ce86e39be8fcfe0a5c755b626 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 4 Jun 2025 23:09:27 -0400 Subject: [PATCH 39/56] add readytorunheader datadescriptors --- .../debug/runtimeinfo/datadescriptor.h | 13 ++++++++++++ src/coreclr/vm/readytoruninfo.h | 1 + .../DataType.cs | 1 + .../Data/ReadyToRunHeader.cs | 21 +++++++++++++++++++ .../Data/ReadyToRunInfo.cs | 9 ++++---- 5 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunHeader.cs diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 38dd82c9efc793..ad11bdf80f5e37 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -566,6 +566,7 @@ CDAC_TYPE_END(FixupPrecodeData) CDAC_TYPE_BEGIN(ReadyToRunInfo) CDAC_TYPE_INDETERMINATE(ReadyToRunInfo) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, ReadyToRunHeader, cdac_data::ReadyToRunHeader) CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, CompositeInfo, cdac_data::CompositeInfo) CDAC_TYPE_FIELD(ReadyToRunInfo, /*uint32*/, NumRuntimeFunctions, cdac_data::NumRuntimeFunctions) CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, RuntimeFunctions, cdac_data::RuntimeFunctions) @@ -575,6 +576,12 @@ CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, DelayLoadMethodCallThunks, cdac_dat CDAC_TYPE_FIELD(ReadyToRunInfo, /*HashMap*/, EntryPointToMethodDescMap, cdac_data::EntryPointToMethodDescMap) CDAC_TYPE_END(ReadyToRunInfo) +CDAC_TYPE_BEGIN(ReadyToRunHeader) +CDAC_TYPE_INDETERMINATE(READYTORUN_HEADER) +CDAC_TYPE_FIELD(ReadyToRunHeader, /*uint16*/, MajorVersion, offsetof(READYTORUN_HEADER, MajorVersion)) +CDAC_TYPE_FIELD(ReadyToRunHeader, /*uint16*/, MinorVersion, offsetof(READYTORUN_HEADER, MinorVersion)) +CDAC_TYPE_END(ReadyToRunHeader) + CDAC_TYPE_BEGIN(ImageDataDirectory) CDAC_TYPE_SIZE(sizeof(IMAGE_DATA_DIRECTORY)) CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, VirtualAddress, offsetof(IMAGE_DATA_DIRECTORY, VirtualAddress)) @@ -837,6 +844,12 @@ CDAC_GLOBAL_STRING(Architecture, riscv64) #error TARGET_{ARCH} define is not recognized by the cDAC. Update this switch and the enum values in IRuntimeInfo.cs #endif +#ifdef _DEBUG +CDAC_GLOBAL_STRING(Configuration, debug) +#else // _DEBUG +CDAC_GLOBAL_STRING(Configuration, release) +#endif // _DEBUG + CDAC_GLOBAL_STRING(RID, RID_STRING) CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain) diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index 7ff51806dbd088..a67a539f743a17 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -344,6 +344,7 @@ class ReadyToRunInfo template<> struct cdac_data { + static constexpr size_t ReadyToRunHeader = offsetof(ReadyToRunInfo, m_pHeader); static constexpr size_t CompositeInfo = offsetof(ReadyToRunInfo, m_pCompositeInfo); static constexpr size_t NumRuntimeFunctions = offsetof(ReadyToRunInfo, m_nRuntimeFunctions); static constexpr size_t RuntimeFunctions = offsetof(ReadyToRunInfo, m_pRuntimeFunctions); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index 5e67e0c569d552..cda5f7de597926 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -88,6 +88,7 @@ public enum DataType ProfControlBlock, ILCodeVersionNode, ReadyToRunInfo, + ReadyToRunHeader, ImageDataDirectory, RuntimeFunction, HashMap, diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunHeader.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunHeader.cs new file mode 100644 index 00000000000000..acc35d21d80859 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunHeader.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class ReadyToRunHeader : IData +{ + static ReadyToRunHeader IData.Create(Target target, TargetPointer address) + => new ReadyToRunHeader(target, address); + + public ReadyToRunHeader(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.ReadyToRunHeader); + + MajorVersion = target.Read(address + (ulong)type.Fields[nameof(MajorVersion)].Offset); + MinorVersion = target.Read(address + (ulong)type.Fields[nameof(MinorVersion)].Offset); + } + + public ushort MajorVersion { get; } + public ushort MinorVersion { get; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs index 01db9d76bd978b..ed932946d195de 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs @@ -10,15 +10,14 @@ internal sealed class ReadyToRunInfo : IData static ReadyToRunInfo IData.Create(Target target, TargetPointer address) => new ReadyToRunInfo(target, address); - private readonly Target _target; - public ReadyToRunInfo(Target target, TargetPointer address) { - _target = target; Target.TypeInfo type = target.GetTypeInfo(DataType.ReadyToRunInfo); CompositeInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(CompositeInfo)].Offset); + ReadyToRunHeader = target.ReadPointer(address + (ulong)type.Fields[nameof(ReadyToRunHeader)].Offset); + NumRuntimeFunctions = target.Read(address + (ulong)type.Fields[nameof(NumRuntimeFunctions)].Offset); RuntimeFunctions = NumRuntimeFunctions > 0 ? target.ReadPointer(address + (ulong)type.Fields[nameof(RuntimeFunctions)].Offset) @@ -34,10 +33,12 @@ public ReadyToRunInfo(Target target, TargetPointer address) // Map is from the composite info pointer (set to itself for non-multi-assembly composite images) EntryPointToMethodDescMap = CompositeInfo + (ulong)type.Fields[nameof(EntryPointToMethodDescMap)].Offset; - } + } internal TargetPointer CompositeInfo { get; } + public TargetPointer ReadyToRunHeader { get; } + public uint NumRuntimeFunctions { get; } public TargetPointer RuntimeFunctions { get; } From 408c566957124effaa985ebffac227c1d85fee06 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 4 Jun 2025 23:10:43 -0400 Subject: [PATCH 40/56] clean up R2RJitManager --- ...ecutionManagerCore.ReadyToRunJitManager.cs | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index f5474e9800de39..12e50c002d4166 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -12,6 +12,7 @@ internal partial class ExecutionManagerCore : IExecutionManager { private sealed class ReadyToRunJitManager : JitManager { + // TODO(cdacx86): How to handle different GCINFO versions? private const uint GCINFO_VERSION = 4; private readonly uint _runtimeFunctionSize; @@ -90,38 +91,25 @@ public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jitt Data.RuntimeFunction runtimeFunction = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, index); TargetPointer unwindInfo = runtimeFunction.UnwindData + imageBase; - uint unwindDataSize = sizeof(uint); // TODO(cdac): This is platform specific and maybe needs its own contract. Current value is for x86 + uint unwindDataSize = GetUnwindDataSize(); gcInfo = unwindInfo + unwindDataSize; - gcVersion = GCINFO_VERSION; // TODO(cdac): This depends on the major version of the runtime. + gcVersion = GCINFO_VERSION; // TODO(cdacx86): This depends on the major version of the runtime. } - private bool IsStubCodeBlockThunk(Data.RangeSection rangeSection, Data.ReadyToRunInfo r2rInfo, TargetCodePointer jittedCodeAddress) + // TODO(cdacx86): This is platform specific and maybe needs its own contract. Current value is for x86 + private uint GetUnwindDataSize() { - if (r2rInfo.DelayLoadMethodCallThunks == TargetPointer.Null) - return false; - - // Check if the address is in the region containing thunks for READYTORUN_HELPER_DelayLoad_MethodCall - Data.ImageDataDirectory thunksData = Target.ProcessedData.GetOrAdd(r2rInfo.DelayLoadMethodCallThunks); - ulong rva = jittedCodeAddress - rangeSection.RangeBegin; - return thunksData.VirtualAddress <= rva && rva < thunksData.VirtualAddress + thunksData.Size; - } - - private TargetPointer GetMethodDescForRuntimeFunction(Data.ReadyToRunInfo r2rInfo, TargetPointer imageBase, uint runtimeFunctionIndex) - { - Data.RuntimeFunction function = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, runtimeFunctionIndex); - - // ReadyToRunInfo::GetMethodDescForEntryPointInNativeImage - TargetCodePointer startAddress = imageBase + function.BeginAddress; - TargetPointer entryPoint = CodePointerUtils.AddressFromCodePointer(startAddress, Target); - - TargetPointer methodDesc = _hashMap.GetValue(r2rInfo.EntryPointToMethodDescMap, entryPoint); - if (methodDesc == (ulong)HashMapLookup.SpecialKeys.InvalidEntry) - return TargetPointer.Null; - - return methodDesc; + RuntimeInfoArchitecture arch = Target.Contracts.RuntimeInfo.GetTargetArchitecture(); + return arch switch + { + RuntimeInfoArchitecture.X86 => sizeof(uint), + RuntimeInfoArchitecture.Arm => 4, + RuntimeInfoArchitecture.Arm64 => 8, + _ => throw new NotSupportedException($"GetUnwindDataSize not supported for architecture: {arch}") + }; } - #region Helpers + #region RuntimeFunction Helpers private Data.ReadyToRunInfo GetReadyToRunInfo(RangeSection rangeSection) { @@ -190,6 +178,32 @@ private uint AdjustRuntimeFunctionToMethodStart(Data.ReadyToRunInfo r2rInfo, Tar return index; } + private bool IsStubCodeBlockThunk(Data.RangeSection rangeSection, Data.ReadyToRunInfo r2rInfo, TargetCodePointer jittedCodeAddress) + { + if (r2rInfo.DelayLoadMethodCallThunks == TargetPointer.Null) + return false; + + // Check if the address is in the region containing thunks for READYTORUN_HELPER_DelayLoad_MethodCall + Data.ImageDataDirectory thunksData = Target.ProcessedData.GetOrAdd(r2rInfo.DelayLoadMethodCallThunks); + ulong rva = jittedCodeAddress - rangeSection.RangeBegin; + return thunksData.VirtualAddress <= rva && rva < thunksData.VirtualAddress + thunksData.Size; + } + + private TargetPointer GetMethodDescForRuntimeFunction(Data.ReadyToRunInfo r2rInfo, TargetPointer imageBase, uint runtimeFunctionIndex) + { + Data.RuntimeFunction function = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, runtimeFunctionIndex); + + // ReadyToRunInfo::GetMethodDescForEntryPointInNativeImage + TargetCodePointer startAddress = imageBase + function.BeginAddress; + TargetPointer entryPoint = CodePointerUtils.AddressFromCodePointer(startAddress, Target); + + TargetPointer methodDesc = _hashMap.GetValue(r2rInfo.EntryPointToMethodDescMap, entryPoint); + if (methodDesc == (ulong)HashMapLookup.SpecialKeys.InvalidEntry) + return TargetPointer.Null; + + return methodDesc; + } + #endregion } } From 55e26f92603ceae3012e32825add493afefb2861 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Thu, 5 Jun 2025 13:42:18 -0400 Subject: [PATCH 41/56] improve GCInfoVersion handling --- docs/design/datacontracts/ExecutionManager.md | 13 +++++++--- .../debug/runtimeinfo/datadescriptor.h | 2 ++ .../Contracts/IExecutionManager.cs | 1 + .../Constants.cs | 2 ++ .../ExecutionManagerCore.EEJitManager.cs | 4 +-- ...ecutionManagerCore.ReadyToRunJitManager.cs | 26 ++++++++++++------- 6 files changed, 33 insertions(+), 15 deletions(-) diff --git a/docs/design/datacontracts/ExecutionManager.md b/docs/design/datacontracts/ExecutionManager.md index 555e2c6d88b549..1b5e360f6ec308 100644 --- a/docs/design/datacontracts/ExecutionManager.md +++ b/docs/design/datacontracts/ExecutionManager.md @@ -30,6 +30,7 @@ struct CodeBlockHandle // Gets the base address the UnwindInfo of codeInfoHandle is relative to TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle); // Gets the GCInfo associated with the code block and its version + // **Currently GetGCInfo only supports X86** void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion); // Gets the offset of the codeInfoHandle inside of the code block TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle); @@ -70,6 +71,7 @@ Data descriptors used: | `RealCodeHeader` | `UnwindInfos` | Start address of Unwind Infos | | `RealCodeHeader` | `GCInfo` | Pointer to the GCInfo encoding | | `Module` | `ReadyToRunInfo` | Pointer to the `ReadyToRunInfo` for the module | +| `ReadyToRunInfo` | `ReadyToRunHeader` | Pointer to the ReadyToRunHeader | | `ReadyToRunInfo` | `CompositeInfo` | Pointer to composite R2R info - or itself for non-composite | | `ReadyToRunInfo` | `NumRuntimeFunctions` | Number of `RuntimeFunctions` | | `ReadyToRunInfo` | `RuntimeFunctions` | Pointer to an array of `RuntimeFunctions` - [see R2R format](../coreclr/botr/readytorun-format.md#readytorunsectiontyperuntimefunctions)| @@ -77,6 +79,8 @@ Data descriptors used: | `ReadyToRunInfo` | `HotColdMap` | Pointer to an array of 32-bit integers - [see R2R format](../coreclr/botr/readytorun-format.md#readytorunsectiontypehotcoldmap-v80) | | `ReadyToRunInfo` | `DelayLoadMethodCallThunks` | Pointer to an `ImageDataDirectory` for the delay load method call thunks | | `ReadyToRunInfo` | `EntryPointToMethodDescMap` | `HashMap` of entry point addresses to `MethodDesc` pointers | +| `ReadyToRunHeader` | `MajorVersion` | ReadyToRun major version | +| `ReadyToRunHeader` | `MinorVersion` | ReadyToRun minor version | | `ImageDataDirectory` | `VirtualAddress` | Virtual address of the image data directory | | `ImageDataDirectory` | `Size` | Size of the data | | `RuntimeFunction` | `BeginAddress` | Begin address of the function | @@ -95,6 +99,7 @@ Global variables used: | `HashMapSlotsPerBucket` | uint32 | Number of slots in each bucket of a `HashMap` | | `HashMapValueMask` | uint64 | Bitmask used when storing values in a `HashMap` | | `FeatureEHFunclets` | uint8 | 1 if EH funclets are enabled, 0 otherwise | +| `GCInfoVersion` | uint32 | JITted code GCInfo version | Contracts used: | Contract Name | @@ -263,10 +268,12 @@ Unwind info (`RUNTIME_FUNCTION`) use relative addressing. For managed code, thes `IExecutionManager.GetGCInfo` gets a pointer to the relevant GCInfo for a `CodeBlockHandle`. The ExecutionManager delegates to the JitManager implementations as the GCInfo is stored differently on jitted and R2R code. -* For jitted code (`EEJitManager`) a pointer to the `GCInfo` is stored on the `RealCodeHeader` which is accessed in the same was as `GetMethodInfo` described above. This can simply be returned as is. +* For jitted code (`EEJitManager`) a pointer to the `GCInfo` is stored on the `RealCodeHeader` which is accessed in the same was as `GetMethodInfo` described above. This can simply be returned as is. The `GCInfoVersion` is defined by the runtime global `GCInfoVersion`. + +* For R2R code (`ReadyToRunJitManager`), the `GCInfo` is stored directly after the `UnwindData`. This in turn is found by looking up the `UnwindInfo` (`RUNTIME_FUNCTION`) and reading the `UnwindData` offset. We find the `UnwindInfo` as described above in `IExecutionManager.GetUnwindInfo`. Once we have the relevant unwind data, we calculate the size of the unwind data and return a pointer to the following byte (first byte of the GCInfo). The size of the unwind data is a platform specific. Currently only X86 is supported with a constant unwind data size of 32-bits. + * The `GCInfoVersion` of R2R code is mapped from the R2R MajorVersion and MinorVersion which is read from the ReadyToRunHeader which itself is read from the ReadyToRunInfo (can be found as in GetMethodInfo). The current GCInfoVersion mapping is: + * MajorVersion >= 11 and MajorVersion < 15 => 4 -* For R2R code (`ReadyToRunJitManager`), the `GCInfo` is stored directly after the `UnwindData`. This in turn is found by looking up the `UnwindInfo` (`RUNTIME_FUNCTION`) and reading the `UnwindData` offset. We find the `UnwindInfo` as described above in `IExecutionManager.GetUnwindInfo`. Once we have the relevant unwind data, we calculate the size of the unwind data and return a pointer to the following byte (first byte of the GCInfo). -// TODO(cdacX86): Add information on finding the size of the unwind info. `IExecutionManager.GetFuncletStartAddress` finds the start of the code blocks funclet. This will be different than the methods start address `GetStartAddress` if the current code block is inside of a funclet. To find the funclet start address, we get the unwind info corresponding to the code block using `IExecutionManager.GetUnwindInfo`. We then parse the unwind info to find the begin address (relative to the unwind info base address) and return the unwind info base address + unwind info begin address. diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index ad11bdf80f5e37..0736e995433929 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -852,6 +852,8 @@ CDAC_GLOBAL_STRING(Configuration, release) CDAC_GLOBAL_STRING(RID, RID_STRING) +CDAC_GLOBAL(GCInfoVersion, uint32, GCINFO_VERSION) + CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain) CDAC_GLOBAL_POINTER(SystemDomain, cdac_data::SystemDomain) CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs index 4a8faa684acfb6..408d8e8f91846f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs @@ -21,6 +21,7 @@ public interface IExecutionManager : IContract TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); + // **Currently GetGCInfo only supports X86** void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) => throw new NotImplementedException(); TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs index a110102616bd8d..ea0381d93a1097 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -60,6 +60,8 @@ public static class Globals public const string Architecture = nameof(Architecture); public const string OperatingSystem = nameof(OperatingSystem); + + public const string GCInfoVersion = nameof(GCInfoVersion); } public static class FieldNames { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs index b408fcaaa30aa8..128394b97d2234 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs @@ -12,8 +12,6 @@ internal partial class ExecutionManagerCore : IExecutionManager { private sealed class EEJitManager : JitManager { - private const uint GCINFO_VERSION = 4; - private readonly INibbleMap _nibbleMap; private readonly RuntimeFunctionLookup _runtimeFunctions; public EEJitManager(Target target, INibbleMap nibbleMap) : base(target) @@ -107,7 +105,7 @@ public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jitt if (!GetRealCodeHeader(rangeSection, codeStart, out Data.RealCodeHeader? realCodeHeader)) return; - gcVersion = GCINFO_VERSION; + gcVersion = Target.ReadGlobal(Constants.Globals.GCInfoVersion); gcInfo = realCodeHeader.GCInfo; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index 12e50c002d4166..1c001d557f6e4f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -12,17 +12,12 @@ internal partial class ExecutionManagerCore : IExecutionManager { private sealed class ReadyToRunJitManager : JitManager { - // TODO(cdacx86): How to handle different GCINFO versions? - private const uint GCINFO_VERSION = 4; - - private readonly uint _runtimeFunctionSize; private readonly PtrHashMapLookup _hashMap; private readonly HotColdLookup _hotCold; private readonly RuntimeFunctionLookup _runtimeFunctions; public ReadyToRunJitManager(Target target) : base(target) { - _runtimeFunctionSize = Target.GetTypeInfo(DataType.RuntimeFunction).Size!.Value; _hashMap = PtrHashMapLookup.Create(target); _hotCold = HotColdLookup.Create(target); _runtimeFunctions = RuntimeFunctionLookup.Create(target); @@ -93,18 +88,31 @@ public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jitt TargetPointer unwindInfo = runtimeFunction.UnwindData + imageBase; uint unwindDataSize = GetUnwindDataSize(); gcInfo = unwindInfo + unwindDataSize; - gcVersion = GCINFO_VERSION; // TODO(cdacx86): This depends on the major version of the runtime. + gcVersion = GetR2RGCInfoVersion(r2rInfo); + } + + // This function must be kept up to date with R2R version changes. + // When the R2R version is bumped, it must be mapped to the correct GCInfo version. + // Adding "MINIMUM_READYTORUN_MAJOR_VERSION" to ensure this is updated according + // to instructions in readytorun.h + private uint GetR2RGCInfoVersion(Data.ReadyToRunInfo r2rInfo) + { + Data.ReadyToRunHeader header = Target.ProcessedData.GetOrAdd(r2rInfo.ReadyToRunHeader); + + // see readytorun.h for the versioning details + return header.MajorVersion switch + { + >= 11 and < 15 => 4, + _ => throw new NotSupportedException($"Unsupported ReadyToRun major version: {header.MajorVersion}.{header.MinorVersion}"), + }; } - // TODO(cdacx86): This is platform specific and maybe needs its own contract. Current value is for x86 private uint GetUnwindDataSize() { RuntimeInfoArchitecture arch = Target.Contracts.RuntimeInfo.GetTargetArchitecture(); return arch switch { RuntimeInfoArchitecture.X86 => sizeof(uint), - RuntimeInfoArchitecture.Arm => 4, - RuntimeInfoArchitecture.Arm64 => 8, _ => throw new NotSupportedException($"GetUnwindDataSize not supported for architecture: {arch}") }; } From 675673b6e342f8caeff1db2617859797c547def2 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Thu, 5 Jun 2025 13:46:01 -0400 Subject: [PATCH 42/56] always check for JitHalt --- src/coreclr/vm/gc_unwind_x86.inl | 4 ---- .../Contracts/StackWalk/Context/X86/X86Unwinder.cs | 6 ------ 2 files changed, 10 deletions(-) diff --git a/src/coreclr/vm/gc_unwind_x86.inl b/src/coreclr/vm/gc_unwind_x86.inl index c92986d358ab24..7adfd0d9b95d49 100644 --- a/src/coreclr/vm/gc_unwind_x86.inl +++ b/src/coreclr/vm/gc_unwind_x86.inl @@ -2770,14 +2770,12 @@ void UnwindEspFrameProlog( unsigned offset = 0; -#ifdef _DEBUG // If the first two instructions are 'nop, int3', then we will // assume that is from a JitHalt operation and skip past it if (methodStart[0] == X86_INSTR_NOP && methodStart[1] == X86_INSTR_INT3) { offset += 2; } -#endif const DWORD curOffs = info->prologOffs; unsigned ESP = pContext->SP; @@ -2933,14 +2931,12 @@ void UnwindEbpDoubleAlignFrameProlog( DWORD offset = 0; -#ifdef _DEBUG // If the first two instructions are 'nop, int3', then we will // assume that is from a JitHalt operation and skip past it if (methodStart[0] == X86_INSTR_NOP && methodStart[1] == X86_INSTR_INT3) { offset += 2; } -#endif /* Check for the case where EBP has not been updated yet. */ diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs index cd9d6e19da7d15..755efaab95df1e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs @@ -209,8 +209,6 @@ have already been popped */ // Have we executed the pop EBP? if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) { - // TODO(cdacx86): are these equivalent? - // pContext->SetEbpLocation(PTR_DWORD(TADDR(ESP))); context.Ebp = _target.Read(esp); esp += _pointerSize; } @@ -335,8 +333,6 @@ private void UnwindEspFrameProlog(ref X86Context context, GCInfo gcInfo, TargetP uint offset = 0; - // TODO(cdacX86): JitHalt DEBUG only check. Can this always exist or should it really depend - // on the target runtimes configuration? // If the first two instructions are 'nop, int3', then we will // assume that is from a JitHalt operation and skip past it if (ReadByteAt(methodStart) == X86_INSTR_NOP && ReadByteAt(methodStart + 1) == X86_INSTR_INT3) @@ -515,8 +511,6 @@ private void UnwindEbpDoubleAlignFrameProlog(ref X86Context context, GCInfo gcIn uint offset = 0; - // TODO(cdacX86): JitHalt DEBUG only check. Can this always exist or should it really depend - // on the target runtimes configuration? // If the first two instructions are 'nop, int3', then we will // assume that is from a JitHalt operation and skip past it if (ReadByteAt(methodStart) == X86_INSTR_NOP && ReadByteAt(methodStart + 1) == X86_INSTR_INT3) From 0bcaaf0a91079748b05e79d3d48be7104fe22090 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Thu, 5 Jun 2025 13:57:40 -0400 Subject: [PATCH 43/56] remove magic numbers in RTS contract --- docs/design/datacontracts/RuntimeTypeSystem.md | 13 ++++++++++--- .../Contracts/RuntimeTypeSystem_1.cs | 13 ++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index a8be67be263081..d438b79d540c42 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -712,6 +712,13 @@ And the following enumeration definitions WrapperStubWithInstantiations = 0x04, } + internal enum ILStubType : uint + { + StubNotSet = 0x0, + StubCLRToNativeInterop = 0x1, + StubCLRToCOMInterop = 0x2, + } + [Flags] internal enum DynamicMethodDescExtendedFlags : uint { @@ -984,15 +991,15 @@ And the various apis are implemented with the following algorithms uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address - uint ilStubType = (uint)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask); + ILStubType ilStubType = (ILStubType)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask); bool isStatic = ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.Static); bool isDelegate = ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsDelegate); bool isCALLI = ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsCALLI); - bool isPInvokeStub = isStatic && !isCALLI && ilStubType == 0x1; - bool isCLRToCOMStub = !isStatic && ilStubType == 0x2; + bool isPInvokeStub = isStatic && !isCALLI && ilStubType == ILStubType.StubCLRToNativeInterop; + bool isCLRToCOMStub = !isStatic && ilStubType == ILStubType.StubCLRToCOMInterop; return isCLRToCOMStub || (isPInvokeStub && !isDelegate); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index 45e4def7a75583..5f75d541c1812f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -73,6 +73,13 @@ internal enum InstantiatedMethodDescFlags2 : ushort WrapperStubWithInstantiations = 0x04, } + internal enum ILStubType : uint + { + StubNotSet = 0x0, + StubCLRToNativeInterop = 0x1, + StubCLRToCOMInterop = 0x2, + } + [Flags] internal enum DynamicMethodDescExtendedFlags : uint { @@ -235,7 +242,7 @@ private DynamicMethodDesc(Target target, TargetPointer methodDescPointer) public string MethodName { get; } public DynamicMethodDescExtendedFlags ExtendedFlags => (DynamicMethodDescExtendedFlags)_storedSigDesc.ExtendedFlags; - public uint ILStubType => (uint)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask); + public ILStubType StubType => (ILStubType)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask); public bool IsStatic => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.Static); public bool IsDynamicMethod => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsLCGMethod); @@ -243,8 +250,8 @@ private DynamicMethodDesc(Target target, TargetPointer methodDescPointer) public bool IsDelegate => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsDelegate); public bool IsCALLI => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsCALLI); - public bool IsPInvokeStub => IsStatic && !IsCALLI && ILStubType == 0x1; - public bool IsCLRToCOMStub => !IsStatic && ILStubType == 0x2; + public bool IsPInvokeStub => IsStatic && !IsCALLI && StubType == ILStubType.StubCLRToNativeInterop; + public bool IsCLRToCOMStub => !IsStatic && StubType == ILStubType.StubCLRToCOMInterop; public bool HasMDContextArg => IsCLRToCOMStub || (IsPInvokeStub && !IsDelegate); } From 26d4b7d7e0cdabe0ea7dc6342d6f65e278f50f88 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Thu, 5 Jun 2025 14:03:49 -0400 Subject: [PATCH 44/56] remove todos --- .../StackWalk/Context/X86/X86Unwinder.cs | 56 +++++++------------ 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs index 755efaab95df1e..a99fea46218921 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs @@ -11,6 +11,26 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X8 public class X86Unwinder(Target target) { + private const byte X86_INSTR_INT3 = 0xCC; // int3 + private const byte X86_INSTR_POP_ECX = 0x59; // pop ecx + private const byte X86_INSTR_RET = 0xC2; // ret imm16 + private const byte X86_INSTR_JMP_NEAR_REL32 = 0xE9; // near jmp rel32 + private const byte X86_INSTR_PUSH_EAX = 0x50; // push eax + private const byte X86_INSTR_XOR = 0x33; // xor + private const byte X86_INSTR_NOP = 0x90; // nop + private const byte X86_INSTR_RETN = 0xC3; // ret + private const byte X86_INSTR_PUSH_EBP = 0x55; // push ebp + private const ushort X86_INSTR_W_MOV_EBP_ESP = 0xEC8B; // mov ebp, esp + private const byte X86_INSTR_CALL_REL32 = 0xE8; // call rel32 + private const ushort X86_INSTR_W_CALL_IND_IMM = 0x15FF; // call [addr32] + private const ushort X86_INSTR_w_JMP_FAR_IND_IMM = 0x25FF; // far jmp [addr32] + private const ushort X86_INSTR_w_TEST_ESP_EAX = 0x0485; // test [esp], eax + private const ushort X86_INSTR_w_LEA_ESP_EBP_BYTE_OFFSET = 0x658d; // lea esp, [ebp-bOffset] + private const ushort X86_INSTR_w_LEA_ESP_EBP_DWORD_OFFSET = 0xa58d; // lea esp, [ebp-dwOffset] + private const ushort X86_INSTR_w_TEST_ESP_DWORD_OFFSET_EAX = 0x8485; // test [esp-dwOffset], eax + private const ushort X86_INSTR_w_LEA_EAX_ESP_BYTE_OFFSET = 0x448d; // lea eax, [esp-bOffset] + private const ushort X86_INSTR_w_LEA_EAX_ESP_DWORD_OFFSET = 0x848d; // lea eax, [esp-dwOffset] + private readonly Target _target = target; private readonly uint _pointerSize = (uint)target.PointerSize; private readonly bool _updateAllRegs = true; @@ -374,14 +394,6 @@ private void UnwindEspFrameProlog(ref X86Context context, GCInfo gcInfo, TargetP } } - // - // Stack probe checks here - // - - // Poison the value, we don't set it properly at the end of the prolog -#if DEBUG - offset = 0xCCCCCCCC; -#endif // Always restore EBP if (regsMask.HasFlag(RegMask.EBP)) @@ -405,11 +417,7 @@ private void UnwindEspFrameProlog(ref X86Context context, GCInfo gcInfo, TargetP if (regsMask.HasFlag(RegMask.EDI)) { context.Edi = _target.Read(savedRegPtr); - savedRegPtr += _pointerSize; } - - // TODO(cdacX86): Should cDAC trash caller saved registers? - // TRASH_CALLEE_UNSAVED_REGS(pContext); } context.Esp = esp; @@ -579,9 +587,6 @@ registers have been pushed already */ offset = SKIP_PUSH_REG(methodStart, offset); } - - // TODO(cdacX86): Should cDAC trash caller saved registers? - // TRASH_CALLEE_UNSAVED_REGS(pContext); } /* The caller's saved EBP is pointed to by our EBP */ @@ -891,27 +896,6 @@ private static void SetRegValue(ref X86Context context, RegMask regMask, TargetP #region Verification Helpers - private const byte X86_INSTR_INT3 = 0xCC; // int3 - private const byte X86_INSTR_POP_ECX = 0x59; // pop ecx - private const byte X86_INSTR_RET = 0xC2; // ret imm16 - private const byte X86_INSTR_JMP_NEAR_REL32 = 0xE9; // near jmp rel32 - private const byte X86_INSTR_PUSH_EAX = 0x50; // push eax - private const byte X86_INSTR_XOR = 0x33; // xor - private const byte X86_INSTR_NOP = 0x90; // nop - private const byte X86_INSTR_RETN = 0xC3; // ret - private const byte X86_INSTR_PUSH_EBP = 0x55; // push ebp - private const ushort X86_INSTR_W_MOV_EBP_ESP = 0xEC8B; // mov ebp, esp - private const byte X86_INSTR_CALL_REL32 = 0xE8; // call rel32 - private const ushort X86_INSTR_W_CALL_IND_IMM = 0x15FF; // call [addr32] - private const ushort X86_INSTR_w_JMP_FAR_IND_IMM = 0x25FF; // far jmp [addr32] - private const ushort X86_INSTR_w_TEST_ESP_EAX = 0x0485; // test [esp], eax - private const ushort X86_INSTR_w_LEA_ESP_EBP_BYTE_OFFSET = 0x658d; // lea esp, [ebp-bOffset] - private const ushort X86_INSTR_w_LEA_ESP_EBP_DWORD_OFFSET = 0xa58d; // lea esp, [ebp-dwOffset] - private const ushort X86_INSTR_w_TEST_ESP_DWORD_OFFSET_EAX = 0x8485; // test [esp-dwOffset], eax - private const ushort X86_INSTR_w_LEA_EAX_ESP_BYTE_OFFSET = 0x448d; // lea eax, [esp-bOffset] - private const ushort X86_INSTR_w_LEA_EAX_ESP_DWORD_OFFSET = 0x848d; // lea eax, [esp-dwOffset] - - /* Check if the given instruction opcode is the one we expect. This is a "necessary" but not "sufficient" check as it ignores the check if the instruction is one of our special markers (for debugging and GcStress) */ From 7e4d18fee5e921d77c503e758f979fae21b0a15f Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Thu, 5 Jun 2025 14:24:54 -0400 Subject: [PATCH 45/56] remove unused datadescriptor --- src/coreclr/debug/runtimeinfo/datadescriptor.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 0736e995433929..60193392bd9732 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -844,12 +844,6 @@ CDAC_GLOBAL_STRING(Architecture, riscv64) #error TARGET_{ARCH} define is not recognized by the cDAC. Update this switch and the enum values in IRuntimeInfo.cs #endif -#ifdef _DEBUG -CDAC_GLOBAL_STRING(Configuration, debug) -#else // _DEBUG -CDAC_GLOBAL_STRING(Configuration, release) -#endif // _DEBUG - CDAC_GLOBAL_STRING(RID, RID_STRING) CDAC_GLOBAL(GCInfoVersion, uint32, GCINFO_VERSION) From 905b3dc3321107c8fc768a1125dca567be3e2d57 Mon Sep 17 00:00:00 2001 From: maxcharlamb Date: Fri, 6 Jun 2025 11:11:12 -0400 Subject: [PATCH 46/56] fix managed tests --- .../tests/MockDescriptors/MockDescriptors.ExecutionManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs index e061b65972a482..d7017cfceab2d9 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs @@ -17,7 +17,7 @@ internal class ExecutionManager { public const ulong ExecutionManagerCodeRangeMapAddress = 0x000a_fff0; - const int RealCodeHeaderSize = 0x08; // must be big enough for the offsets of RealCodeHeader size in ExecutionManagerTestTarget, below + const int RealCodeHeaderSize = 0x10; // must be big enough for the offsets of RealCodeHeader size in ExecutionManagerTestTarget, below public struct AllocationRange { @@ -232,6 +232,7 @@ public static RangeSectionMapTestBuilder CreateRangeSection(MockTarget.Architect Fields = [ new(nameof(Data.RealCodeHeader.MethodDesc), DataType.pointer), + new(nameof(Data.RealCodeHeader.GCInfo), DataType.pointer), ] }; @@ -240,6 +241,7 @@ public static RangeSectionMapTestBuilder CreateRangeSection(MockTarget.Architect DataType = DataType.ReadyToRunInfo, Fields = [ + new(nameof(Data.ReadyToRunInfo.ReadyToRunHeader), DataType.pointer), new(nameof(Data.ReadyToRunInfo.CompositeInfo), DataType.pointer), new(nameof(Data.ReadyToRunInfo.NumRuntimeFunctions), DataType.uint32), new(nameof(Data.ReadyToRunInfo.RuntimeFunctions), DataType.pointer), From 4b2df0f1fd6dfd4be416e59a3ff71c69f8d9cc11 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Mon, 9 Jun 2025 14:33:33 -0400 Subject: [PATCH 47/56] update to match #116388 --- .../design/datacontracts/RuntimeTypeSystem.md | 35 -------- .../Contracts/IRuntimeTypeSystem.cs | 4 - .../Contracts/RuntimeTypeSystem_1.cs | 34 -------- .../Context/X86/GCInfoDecoding/GCInfo.cs | 2 - .../Contracts/StackWalk/StackWalk_1.cs | 80 +------------------ 5 files changed, 2 insertions(+), 153 deletions(-) diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index d438b79d540c42..aaf275adbcc116 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -138,10 +138,6 @@ partial interface IRuntimeTypeSystem : IContract // A IL Stub method is also a StoredSigMethodDesc, and a NoMetadataMethod public virtual bool IsILStub(MethodDescHandle methodDesc); - // Return true if the MethodDesc represents an IL Stub that takes a context argument - // that is an interop MethodDesc. - public virtual bool HasMDContextArg(MethodDescHandle methodDesc); - // Return true if a MethodDesc is in a collectible module public virtual bool IsCollectibleMethod(MethodDescHandle methodDesc); @@ -712,13 +708,6 @@ And the following enumeration definitions WrapperStubWithInstantiations = 0x04, } - internal enum ILStubType : uint - { - StubNotSet = 0x0, - StubCLRToNativeInterop = 0x1, - StubCLRToCOMInterop = 0x2, - } - [Flags] internal enum DynamicMethodDescExtendedFlags : uint { @@ -980,30 +969,6 @@ And the various apis are implemented with the following algorithms return ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsILStub); } - public bool HasMDContextArg(MethodDescHandle methodDescHandle) - { - MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; - - if (methodDesc.Classification != MethodDescClassification.Dynamic) - { - return false; - } - - uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address - - ILStubType ilStubType = (ILStubType)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask); - - bool isStatic = ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.Static); - bool isDelegate = ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsDelegate); - bool isCALLI = ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsCALLI); - - - bool isPInvokeStub = isStatic && !isCALLI && ilStubType == ILStubType.StubCLRToNativeInterop; - bool isCLRToCOMStub = !isStatic && ilStubType == ILStubType.StubCLRToCOMInterop; - - return isCLRToCOMStub || (isPInvokeStub && !isDelegate); - } - public ushort GetSlotNumber(MethodDescHandle methodDesc) => _methodDescs[methodDesc.Addres]._desc.Slot; ``` diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index 522a50f06d9411..61c4274fc763e5 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -156,10 +156,6 @@ public interface IRuntimeTypeSystem : IContract // A IL Stub method is also a StoredSigMethodDesc, and a NoMetadataMethod bool IsILStub(MethodDescHandle methodDesc) => throw new NotImplementedException(); - // Return true if the MethodDesc represents an IL Stub that takes a context argument - // that is an interop MethodDesc. - public bool HasMDContextArg(MethodDescHandle methodDescHandle) => throw new NotImplementedException(); - bool IsCollectibleMethod(MethodDescHandle methodDesc) => throw new NotImplementedException(); bool IsVersionable(MethodDescHandle methodDesc) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index 5f75d541c1812f..1225c281df73ee 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -73,24 +73,11 @@ internal enum InstantiatedMethodDescFlags2 : ushort WrapperStubWithInstantiations = 0x04, } - internal enum ILStubType : uint - { - StubNotSet = 0x0, - StubCLRToNativeInterop = 0x1, - StubCLRToCOMInterop = 0x2, - } - [Flags] internal enum DynamicMethodDescExtendedFlags : uint { - Static = 0x00001000, IsLCGMethod = 0x00004000, IsILStub = 0x00008000, - IsDelegate = 0x00010000, - IsCALLI = 0x00020000, - FlagMask = 0x0003f800, - StackArgSizeMask = 0xfffc0000, - ILStubTypeMask = ~(FlagMask | StackArgSizeMask), } // on MethodDescChunk.FlagsAndTokenRange @@ -242,18 +229,9 @@ private DynamicMethodDesc(Target target, TargetPointer methodDescPointer) public string MethodName { get; } public DynamicMethodDescExtendedFlags ExtendedFlags => (DynamicMethodDescExtendedFlags)_storedSigDesc.ExtendedFlags; - public ILStubType StubType => (ILStubType)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask); - public bool IsStatic => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.Static); public bool IsDynamicMethod => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsLCGMethod); public bool IsILStub => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsILStub); - public bool IsDelegate => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsDelegate); - public bool IsCALLI => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsCALLI); - - public bool IsPInvokeStub => IsStatic && !IsCALLI && StubType == ILStubType.StubCLRToNativeInterop; - public bool IsCLRToCOMStub => !IsStatic && StubType == ILStubType.StubCLRToCOMInterop; - - public bool HasMDContextArg => IsCLRToCOMStub || (IsPInvokeStub && !IsDelegate); } private sealed class StoredSigMethodDesc : IData @@ -809,18 +787,6 @@ public bool IsILStub(MethodDescHandle methodDescHandle) return AsDynamicMethodDesc(methodDesc).IsILStub; } - public bool HasMDContextArg(MethodDescHandle methodDescHandle) - { - MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; - - if (methodDesc.Classification != MethodClassification.Dynamic) - { - return false; - } - - return AsDynamicMethodDesc(methodDesc).HasMDContextArg; - } - private MethodTable GetOrCreateMethodTable(MethodDesc methodDesc) { // Ensures that the method table is valid, created, and cached diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs index 9b6289e76c5352..56e5968d7da4d0 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs @@ -45,8 +45,6 @@ public record GCInfo public bool IsInEpilog => EpilogOffset != unchecked((uint)-1); public uint EpilogOffset { get; set; } = unchecked((uint)-1); - public bool HasReversePInvoke => Header.RevPInvokeOffset != InfoHdr.INVALID_REV_PINVOKE_OFFSET; - public uint RawStackSize { get; set; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs index 5763c31c8d4887..2b1c42c1eae825 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs @@ -7,19 +7,16 @@ using System.Collections.Generic; using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; -using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; namespace Microsoft.Diagnostics.DataContractReader.Contracts; internal readonly struct StackWalk_1 : IStackWalk { private readonly Target _target; - private readonly RuntimeInfoArchitecture _arch; internal StackWalk_1(Target target) { _target = target; - _arch = target.Contracts.RuntimeInfo.GetTargetArchitecture(); } public enum StackWalkState @@ -77,7 +74,6 @@ IEnumerable IStackWalk.CreateStackWalk(ThreadData threadD private bool Next(StackWalkData handle) { - IPlatformAgnosticContext prevContext = handle.Context.Clone(); switch (handle.State) { case StackWalkState.SW_FRAMELESS: @@ -105,12 +101,12 @@ private bool Next(StackWalkData handle) case StackWalkState.SW_COMPLETE: return false; } - UpdateState(handle, prevContext); + UpdateState(handle); return handle.State is not (StackWalkState.SW_ERROR or StackWalkState.SW_COMPLETE); } - private void UpdateState(StackWalkData handle, IPlatformAgnosticContext prevContext) + private void UpdateState(StackWalkData handle) { // If we are complete or in a bad state, no updating is required. if (handle.State is StackWalkState.SW_ERROR or StackWalkState.SW_COMPLETE) @@ -121,35 +117,6 @@ private void UpdateState(StackWalkData handle, IPlatformAgnosticContext prevCont bool isManaged = IsManaged(handle.Context.InstructionPointer, out _); bool validFrame = handle.FrameIter.IsValid(); - // - // Platform specific logic - // - if (_arch == RuntimeInfoArchitecture.X86) - { - if (IsManaged(prevContext.InstructionPointer, out CodeBlockHandle? prevCbh)) - { - IExecutionManager eman = _target.Contracts.ExecutionManager; - - if (!eman.IsFunclet(prevCbh.Value)) - { - eman.GetGCInfo(prevCbh.Value, out TargetPointer gcInfoAddress, out uint _); - uint relOffset = (uint)eman.GetRelativeOffset(prevCbh.Value).Value; - GCInfo gcInfo = new(_target, gcInfoAddress, relOffset); - if (gcInfo.HasReversePInvoke) - { - // The managed frame we've unwound from had reverse PInvoke frame. Since we are on a frameless - // frame, that means that the method was called from managed code without any native frames in between. - // On x86, the InlinedCallFrame of the PInvoke would get skipped as we've just unwound to the PInvoke IL stub and - // for this architecture, the inlined call frames are supposed to be processed before the managed frame they are stored in. - // So we force the stack frame iterator to process the InlinedCallFrame before the IL stub. - Debug.Assert(handle.FrameIter.IsInlineCallFrameWithActiveCall()); - handle.State = StackWalkState.SW_FRAME; - return; - } - } - } - } - if (isManaged) { handle.State = StackWalkState.SW_FRAMELESS; @@ -185,52 +152,9 @@ private bool CheckForSkippedFrames(StackWalkData handle) IPlatformAgnosticContext parentContext = handle.Context.Clone(); parentContext.Unwind(_target); - if (_arch == RuntimeInfoArchitecture.X86) - { - return CheckForSkippedFramesX86(handle, parentContext); - } - return handle.FrameIter.CurrentFrameAddress.Value < parentContext.StackPointer.Value; } - /// - /// X86 specific check for skipped frames. Different than other platforms due to X86 - /// only reporting the InlinedCallFrame once unless it is an IL stub with an MD context argument. - /// - /// true if there are skipped frames. - private bool CheckForSkippedFramesX86(StackWalkData handle, IPlatformAgnosticContext parentContext) - { - IExecutionManager eman = _target.Contracts.ExecutionManager; - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - - while (handle.FrameIter.IsValid() && - handle.FrameIter.CurrentFrameAddress.Value < parentContext.StackPointer.Value) - { - bool reportInteropMD = false; - if (eman.GetCodeBlockHandle(handle.Context.InstructionPointer.Value) is CodeBlockHandle cbh) - { - MethodDescHandle mdHandle = rts.GetMethodDescHandle(eman.GetMethodDesc(cbh)); - reportInteropMD = - handle.FrameIter.IsValid() && - handle.FrameIter.GetCurrentFrameType() == FrameIterator.FrameType.InlinedCallFrame && - rts.IsILStub(mdHandle) && - rts.HasMDContextArg(mdHandle); - } - - if (handle.FrameIter.IsInlineCallFrameWithActiveCall() && !reportInteropMD) - { - // On x86 we have already reported the InlinedCallFrame, don't report it again. - handle.FrameIter.Next(); - } - else - { - return true; - } - } - - return false; - } - byte[] IStackWalk.GetRawContext(IStackDataFrameHandle stackDataFrameHandle) { StackDataFrameHandle handle = AssertCorrectHandle(stackDataFrameHandle); From c8bba508b48b0a46f859803d19845fdc569467d8 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Mon, 9 Jun 2025 14:56:46 -0400 Subject: [PATCH 48/56] clean up docs --- docs/design/datacontracts/RuntimeTypeSystem.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index aaf275adbcc116..b1d65b7c825273 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -711,14 +711,8 @@ And the following enumeration definitions [Flags] internal enum DynamicMethodDescExtendedFlags : uint { - Static = 0x00001000, IsLCGMethod = 0x00004000, IsILStub = 0x00008000, - IsDelegate = 0x00010000, - IsCALLI = 0x00020000, - FlagMask = 0x0003f800, - StackArgSizeMask = 0xfffc0000, - ILStubTypeMask = ~(FlagMask | StackArgSizeMask), } [Flags] From 9160287c79c558d81b56494daeb44b8abb468313 Mon Sep 17 00:00:00 2001 From: maxcharlamb Date: Fri, 13 Jun 2025 14:09:52 -0400 Subject: [PATCH 49/56] use span for const data --- .../Context/X86/GCInfoDecoding/CallPattern.cs | 12 ++++++------ .../Context/X86/GCInfoDecoding/GCArgTable.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/CallPattern.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/CallPattern.cs index 7c3d7df15a5e3b..da2024c826262a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/CallPattern.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/CallPattern.cs @@ -9,11 +9,11 @@ internal static class CallPattern { /// - /// based on src\inc\gcdecoder.cpp decodeCallPattern + /// based on src\inc\gcdecoder.cpp DecodeCallPattern /// public static void DecodeCallPattern(uint pattern, out uint argCnt, out uint regMask, out uint argMask, out uint codeDelta) { - uint val = callPatternTable[pattern]; + uint val = CallPatternTable[(int)pattern]; byte[] fld = BitConverter.GetBytes(val); argCnt = fld[0]; regMask = fld[1]; // EBP,EBX,ESI,EDI @@ -22,14 +22,14 @@ public static void DecodeCallPattern(uint pattern, out uint argCnt, out uint reg } /// - /// based on src\inc\gcdecoder.cpp callCommonDelta + /// based on src\inc\gcdecoder.cpp CallCommonDelta /// - public static readonly uint[] callCommonDelta = [6, 8, 10, 12]; + public static ReadOnlySpan CallCommonDelta => [6, 8, 10, 12]; /// - /// based on src\inc\gcdecoder.cpp callPatternTable + /// based on src\inc\gcdecoder.cpp CallPatternTable /// - private static readonly uint[] callPatternTable = + private static ReadOnlySpan CallPatternTable => [ 0x0a000200, // 30109 0x0c000200, // 22970 diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs index 2c3bc5ab52eaba..3d2ec4afdaf071 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCArgTable.cs @@ -465,7 +465,7 @@ private void GetTransitionsNoEbp(ref TargetPointer offset) val = _target.Read(offset++); callPndMask = val & 0x7; callArgCnt = (val >> 3) & 0x7; - lastSkip = CallPattern.callCommonDelta[val >> 6]; + lastSkip = CallPattern.CallCommonDelta[(int)(val >> 6)]; curOffs += lastSkip; SaveCallTransition(ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); AddNewTransition(new StackDepthTransition((int)curOffs, (int)callArgCnt)); From 0a5b3d524953d3da23bfed58dcb9cc271eade499 Mon Sep 17 00:00:00 2001 From: maxcharlamb Date: Fri, 13 Jun 2025 14:16:13 -0400 Subject: [PATCH 50/56] fix unwinder issue --- .../Contracts/StackWalk/Context/X86/X86Unwinder.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs index a99fea46218921..b723f115102d34 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs @@ -161,8 +161,11 @@ have already been popped */ { // do nothing before popping the callee-saved registers } - else if (gcInfo.RawStackSize == _target.PointerSize) + else if (gcInfo.RawStackSize == _target.PointerSize && ReadByteAt(epilogBase) == X86_INSTR_POP_ECX) { + // We may use "POP ecx" for doing "ADD ESP, 4", + // or we may not (in the case of JMP epilogs) + // "pop ecx" will make ESP point to the callee-saved registers if (!InstructionAlreadyExecuted(offset, gcInfo.EpilogOffset)) { From ef1281e62f06a2a57bc3170e94c21211adab3a9c Mon Sep 17 00:00:00 2001 From: maxcharlamb Date: Fri, 13 Jun 2025 14:35:54 -0400 Subject: [PATCH 51/56] add check for GCInfo version --- .../ExecutionManagerCore.ReadyToRunJitManager.cs | 2 +- .../StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs | 10 ++++++++-- .../Contracts/StackWalk/Context/X86/X86Unwinder.cs | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index 1c001d557f6e4f..95af3f5f8a9ef5 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -102,7 +102,7 @@ private uint GetR2RGCInfoVersion(Data.ReadyToRunInfo r2rInfo) // see readytorun.h for the versioning details return header.MajorVersion switch { - >= 11 and < 15 => 4, + >= 11 => 4, _ => throw new NotSupportedException($"Unsupported ReadyToRun major version: {header.MajorVersion}.{header.MinorVersion}"), }; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs index 56e5968d7da4d0..6ee0bcfca4209a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.IO; using System.Linq; namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; @@ -30,6 +29,8 @@ public enum RegMask public record GCInfo { + private const uint MAXIMUM_SUPPORTED_GCINFO_VERSION = 4; + private readonly Target _target; private readonly TargetPointer _gcInfoAddress; @@ -67,8 +68,13 @@ public record GCInfo public uint PushedArgSize => _pushedArgSize.Value; private readonly Lazy _pushedArgSize; - public GCInfo(Target target, TargetPointer gcInfoAddress, uint relativeOffset) + public GCInfo(Target target, TargetPointer gcInfoAddress, uint gcInfoVersion, uint relativeOffset) { + if (gcInfoVersion > MAXIMUM_SUPPORTED_GCINFO_VERSION) + { + throw new NotSupportedException($"GCInfo version {gcInfoVersion} is not supported. Maximum supported version is {MAXIMUM_SUPPORTED_GCINFO_VERSION}."); + } + _target = target; _gcInfoAddress = gcInfoAddress; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs index b723f115102d34..0b90ec428403fd 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/X86Unwinder.cs @@ -56,13 +56,13 @@ public bool Unwind(ref X86Context context) throw new InvalidOperationException("Unwind failed, unable to find code block for the instruction pointer."); } - eman.GetGCInfo(cbh, out TargetPointer gcInfoAddress, out uint _); + eman.GetGCInfo(cbh, out TargetPointer gcInfoAddress, out uint gcInfoVersion); uint relOffset = (uint)eman.GetRelativeOffset(cbh).Value; TargetPointer methodStart = eman.GetStartAddress(cbh).AsTargetPointer; TargetPointer funcletStart = eman.GetFuncletStartAddress(cbh).AsTargetPointer; bool isFunclet = eman.IsFunclet(cbh); - GCInfo gcInfo = new(_target, gcInfoAddress, relOffset); + GCInfo gcInfo = new(_target, gcInfoAddress, gcInfoVersion, relOffset); if (gcInfo.IsInEpilog) { From 423ff27c98a703aac257204a125e11ee789a8348 Mon Sep 17 00:00:00 2001 From: maxcharlamb Date: Mon, 16 Jun 2025 15:12:35 -0400 Subject: [PATCH 52/56] comments --- src/coreclr/inc/gcinfo.h | 12 +++++++----- .../ExecutionManagerCore.ReadyToRunJitManager.cs | 2 +- .../StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs | 5 +++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/coreclr/inc/gcinfo.h b/src/coreclr/inc/gcinfo.h index cdcbb9eb14f04e..cf4a43334b281f 100644 --- a/src/coreclr/inc/gcinfo.h +++ b/src/coreclr/inc/gcinfo.h @@ -77,13 +77,15 @@ struct GCInfoToken } #endif + // Keep this in sync with GetR2RGCInfoVersion in cDac (ExecutionManagerCore.ReadyToRunJitManager.cs) static uint32_t ReadyToRunVersionToGcInfoVersion(uint32_t readyToRunMajorVersion, uint32_t readyToRunMinorVersion) { -#ifdef SOS_INCLUDE - return GCInfoVersion(); -#else - return GCINFO_VERSION; -#endif + if (readyToRunMajorVersion >= 11) + return 4; + + // Since v2 and v3 had the same file format and v1 is no longer supported, + // we can assume GCInfo v3. + return 3; } }; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index 95af3f5f8a9ef5..fc77c5a6f5bc80 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -102,8 +102,8 @@ private uint GetR2RGCInfoVersion(Data.ReadyToRunInfo r2rInfo) // see readytorun.h for the versioning details return header.MajorVersion switch { + < 11 => 3, >= 11 => 4, - _ => throw new NotSupportedException($"Unsupported ReadyToRun major version: {header.MajorVersion}.{header.MinorVersion}"), }; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs index 6ee0bcfca4209a..f58635691ac74b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86/GCInfoDecoding/GCInfo.cs @@ -29,6 +29,7 @@ public enum RegMask public record GCInfo { + private const uint MINIMUM_SUPPORTED_GCINFO_VERSION = 4; private const uint MAXIMUM_SUPPORTED_GCINFO_VERSION = 4; private readonly Target _target; @@ -70,6 +71,10 @@ public record GCInfo public GCInfo(Target target, TargetPointer gcInfoAddress, uint gcInfoVersion, uint relativeOffset) { + if (gcInfoVersion < MINIMUM_SUPPORTED_GCINFO_VERSION) + { + throw new NotSupportedException($"GCInfo version {gcInfoVersion} is not supported. Minimum supported version is {MINIMUM_SUPPORTED_GCINFO_VERSION}."); + } if (gcInfoVersion > MAXIMUM_SUPPORTED_GCINFO_VERSION) { throw new NotSupportedException($"GCInfo version {gcInfoVersion} is not supported. Maximum supported version is {MAXIMUM_SUPPORTED_GCINFO_VERSION}."); From fe4ead151f8a9e376405f12be688456b6dbdadf2 Mon Sep 17 00:00:00 2001 From: maxcharlamb Date: Mon, 16 Jun 2025 18:59:48 -0400 Subject: [PATCH 53/56] remove FeatureEHFunclets check in modified code --- .../ExecutionManagerCore.ReadyToRunJitManager.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index fc77c5a6f5bc80..bdbe99980c4107 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -158,22 +158,16 @@ private bool GetRuntimeFunction( private uint AdjustRuntimeFunctionIndexForHotCold(Data.ReadyToRunInfo r2rInfo, uint index) { - bool featureEHFunclets = Target.ReadGlobal(Constants.Globals.FeatureEHFunclets) != 0; - if (featureEHFunclets) - { - // Look up index in hot/cold map - if the function is in the cold part, get the index of the hot part. - index = _hotCold.GetHotFunctionIndex(r2rInfo.NumHotColdMap, r2rInfo.HotColdMap, index); - Debug.Assert(index < r2rInfo.NumRuntimeFunctions); - } + // Look up index in hot/cold map - if the function is in the cold part, get the index of the hot part. + index = _hotCold.GetHotFunctionIndex(r2rInfo.NumHotColdMap, r2rInfo.HotColdMap, index); + Debug.Assert(index < r2rInfo.NumRuntimeFunctions); return index; } private uint AdjustRuntimeFunctionToMethodStart(Data.ReadyToRunInfo r2rInfo, TargetPointer imageBase, uint index, out TargetPointer methodDesc) { - bool featureEHFunclets = Target.ReadGlobal(Constants.Globals.FeatureEHFunclets) != 0; - methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index); - while (featureEHFunclets && methodDesc == TargetPointer.Null) + while (methodDesc == TargetPointer.Null) { // Funclets won't have a direct entry in the map of runtime function entry point to method desc. // The funclet's address (and index) will be greater than that of the corresponding function, so From 4c96d48d88162e9851acf6c12ecbeb2dc392c591 Mon Sep 17 00:00:00 2001 From: maxcharlamb Date: Mon, 16 Jun 2025 19:04:38 -0400 Subject: [PATCH 54/56] add comment explaining loosened GetUsefulGlobals assertions --- .../cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 66355c5b466b51..d74eb922e6e210 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -1662,7 +1662,12 @@ int ISOSDacInterface.GetUsefulGlobals(DacpUsefulGlobalsData* data) { DacpUsefulGlobalsData dataLocal; int hrLocal = _legacyImpl.GetUsefulGlobals(&dataLocal); - // Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + // SOS can call GetUsefulGlobals before the global pointers are initialized. + // In the DAC build, this behavior varies depending on the compiler. + // In MSVC builds, the DAC global table is a compile time constant and the DAC will return successfully. + // In Clang builds, the DAC global table is constructed at runtime and the DAC will fail. + // Because of this variation, we cannot match the DAC behavior exactly. + // As long as the returned data matches, it should be fine. if (hr == HResults.S_OK || hrLocal == HResults.S_OK) { Debug.Assert(data->ArrayMethodTable == dataLocal.ArrayMethodTable); From cf8dd6411ac1848e11f2ec83e1bdec3b7726eb9f Mon Sep 17 00:00:00 2001 From: maxcharlamb Date: Mon, 16 Jun 2025 19:05:50 -0400 Subject: [PATCH 55/56] word --- .../cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index d74eb922e6e210..54d1e2c87949eb 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -1663,9 +1663,9 @@ int ISOSDacInterface.GetUsefulGlobals(DacpUsefulGlobalsData* data) DacpUsefulGlobalsData dataLocal; int hrLocal = _legacyImpl.GetUsefulGlobals(&dataLocal); // SOS can call GetUsefulGlobals before the global pointers are initialized. - // In the DAC build, this behavior varies depending on the compiler. - // In MSVC builds, the DAC global table is a compile time constant and the DAC will return successfully. - // In Clang builds, the DAC global table is constructed at runtime and the DAC will fail. + // In the DAC, this behavior depends on the compiler. + // MSVC builds: the DAC global table is a compile time constant and the DAC will return successfully. + // Clang builds: the DAC global table is constructed at runtime and the DAC will fail. // Because of this variation, we cannot match the DAC behavior exactly. // As long as the returned data matches, it should be fine. if (hr == HResults.S_OK || hrLocal == HResults.S_OK) From 8b7f03f192336975ef8aa0fdd38a3d1a24a63423 Mon Sep 17 00:00:00 2001 From: maxcharlamb Date: Tue, 17 Jun 2025 10:38:19 -0400 Subject: [PATCH 56/56] fix merge conflict --- .../StackWalk/Context/AMD64/AMD64Unwinder.cs | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64/AMD64Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64/AMD64Unwinder.cs index 5f7d18a29ea53f..d5a67fb65e411e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64/AMD64Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64/AMD64Unwinder.cs @@ -51,7 +51,7 @@ public bool Unwind(ref AMD64Context context) TargetPointer controlPC = context.InstructionPointer; TargetPointer imageBase = _eman.GetUnwindInfoBaseAddress(cbh); - Data.RuntimeFunction functionEntry = _target.ProcessedData.GetOrAdd(_eman.GetUnwindInfo(cbh, controlPC.Value)); + Data.RuntimeFunction functionEntry = _target.ProcessedData.GetOrAdd(_eman.GetUnwindInfo(cbh)); if (functionEntry.EndAddress is null) return false; if (GetUnwindInfoHeader(imageBase + functionEntry.UnwindData) is not UnwindInfoHeader unwindInfo) @@ -861,11 +861,11 @@ private bool UnwindPrologue( // the register than was pushed. // case UnwindCode.OpCodes.UWOP_PUSH_NONVOL: - { - SetRegister(ref context, unwindOp.OpInfo, _target.ReadPointer(context.Rsp)); - context.Rsp += 8; - break; - } + { + SetRegister(ref context, unwindOp.OpInfo, _target.ReadPointer(context.Rsp)); + context.Rsp += 8; + break; + } // // Allocate a large sized area on the stack. @@ -874,26 +874,26 @@ private bool UnwindPrologue( // 16- or 32-bits. // case UnwindCode.OpCodes.UWOP_ALLOC_LARGE: - { - index++; - UnwindCode nextUnwindOp = GetUnwindCode(unwindInfo, index); - uint frameOffset = nextUnwindOp.FrameOffset; - if (unwindOp.OpInfo != 0) { index++; - nextUnwindOp = GetUnwindCode(unwindInfo, index); - frameOffset += (uint)(nextUnwindOp.FrameOffset << 16); - } - else - { - // The 16-bit form is scaled. - frameOffset *= 8; + UnwindCode nextUnwindOp = GetUnwindCode(unwindInfo, index); + uint frameOffset = nextUnwindOp.FrameOffset; + if (unwindOp.OpInfo != 0) + { + index++; + nextUnwindOp = GetUnwindCode(unwindInfo, index); + frameOffset += (uint)(nextUnwindOp.FrameOffset << 16); + } + else + { + // The 16-bit form is scaled. + frameOffset *= 8; + } + + context.Rsp += frameOffset; + break; } - context.Rsp += frameOffset; - break; - } - // // Allocate a small sized area on the stack. // @@ -901,10 +901,10 @@ private bool UnwindPrologue( // allocation size (8 is the scale factor) minus 8. // case UnwindCode.OpCodes.UWOP_ALLOC_SMALL: - { - context.Rsp += (unwindOp.OpInfo * 8u) + 8u; - break; - } + { + context.Rsp += (unwindOp.OpInfo * 8u) + 8u; + break; + } // // Establish the frame pointer register. @@ -912,11 +912,11 @@ private bool UnwindPrologue( // The operation information is not used. // case UnwindCode.OpCodes.UWOP_SET_FPREG: - { - context.Rsp = GetRegister(context, unwindInfo.FrameRegister); - context.Rsp -= unwindInfo.FrameOffset * 16u; - break; - } + { + context.Rsp = GetRegister(context, unwindInfo.FrameRegister); + context.Rsp -= unwindInfo.FrameOffset * 16u; + break; + } // // Establish the frame pointer register using a large size displacement. @@ -926,19 +926,19 @@ private bool UnwindPrologue( // Unix only. // case UnwindCode.OpCodes.UWOP_SET_FPREG_LARGE: - { - UnwinderAssert(_unix); - UnwinderAssert(unwindInfo.FrameOffset == 15); - uint frameOffset = GetUnwindCode(unwindInfo, index + 1).FrameOffset; - frameOffset += (uint)(GetUnwindCode(unwindInfo, index + 2).FrameOffset << 16); - UnwinderAssert((frameOffset & 0xF0000000) == 0); + { + UnwinderAssert(_unix); + UnwinderAssert(unwindInfo.FrameOffset == 15); + uint frameOffset = GetUnwindCode(unwindInfo, index + 1).FrameOffset; + frameOffset += (uint)(GetUnwindCode(unwindInfo, index + 2).FrameOffset << 16); + UnwinderAssert((frameOffset & 0xF0000000) == 0); - context.Rsp = GetRegister(context, unwindInfo.FrameRegister); - context.Rsp -= frameOffset * 16; + context.Rsp = GetRegister(context, unwindInfo.FrameRegister); + context.Rsp -= frameOffset * 16; - index += 2; - break; - } + index += 2; + break; + } // // Save nonvolatile integer register on the stack using a @@ -947,12 +947,12 @@ private bool UnwindPrologue( // The operation information is the register number. // case UnwindCode.OpCodes.UWOP_SAVE_NONVOL: - { - uint frameOffset = GetUnwindCode(unwindInfo, index + 1).FrameOffset * 8u; - SetRegister(ref context, unwindOp.OpInfo, _target.ReadPointer(frameBase + frameOffset)); - index += 1; - break; - } + { + uint frameOffset = GetUnwindCode(unwindInfo, index + 1).FrameOffset * 8u; + SetRegister(ref context, unwindOp.OpInfo, _target.ReadPointer(frameBase + frameOffset)); + index += 1; + break; + } // // Function epilog marker (ignored for prologue unwind). @@ -998,21 +998,21 @@ private bool UnwindPrologue( // machine frame contains an error code or not. // case UnwindCode.OpCodes.UWOP_PUSH_MACHFRAME: - { - machineFrame = false; - TargetPointer returnAddressPtr = context.Rsp; - TargetPointer stackAddressPtr = context.Rsp + (3 * 8); - if (unwindOp.OpInfo != 0) { - returnAddressPtr += (uint)_target.PointerSize; - stackAddressPtr += (uint)_target.PointerSize; + machineFrame = false; + TargetPointer returnAddressPtr = context.Rsp; + TargetPointer stackAddressPtr = context.Rsp + (3 * 8); + if (unwindOp.OpInfo != 0) + { + returnAddressPtr += (uint)_target.PointerSize; + stackAddressPtr += (uint)_target.PointerSize; + } + + context.Rip = _target.ReadPointer(returnAddressPtr); + context.Rsp = _target.ReadPointer(stackAddressPtr); + break; } - context.Rip = _target.ReadPointer(returnAddressPtr); - context.Rsp = _target.ReadPointer(stackAddressPtr); - break; - } - default: Debug.Fail("Unexpected unwind operation code."); break; @@ -1224,7 +1224,7 @@ private Data.RuntimeFunction LookupPrimaryFunctionEntry(Data.RuntimeFunction fun return null; TargetPointer targetImageBase = _eman.GetUnwindInfoBaseAddress(cbh); - Data.RuntimeFunction targetFunctionEntry = _target.ProcessedData.GetOrAdd(_eman.GetUnwindInfo(cbh, controlPC.Value)); + Data.RuntimeFunction targetFunctionEntry = _target.ProcessedData.GetOrAdd(_eman.GetUnwindInfo(cbh)); targetFunctionEntry = LookupPrimaryFunctionEntry(targetFunctionEntry, targetImageBase);