From 22040a0714b0e33cc73461009be94508257fc8b1 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Tue, 24 Mar 2026 17:07:29 -0700 Subject: [PATCH 1/4] adding GetJitHelperFunctionName cDAC API --- docs/design/datacontracts/AuxiliarySymbols.md | 58 ++++++++ src/coreclr/inc/dacvars.h | 2 + .../vm/datadescriptor/datadescriptor.inc | 13 +- src/coreclr/vm/jithelpers.cpp | 18 +++ src/coreclr/vm/jitinterface.cpp | 2 + src/coreclr/vm/jitinterface.h | 13 ++ src/coreclr/vm/threads.cpp | 4 + .../ContractRegistry.cs | 4 + .../Contracts/IAuxiliarySymbols.cs | 18 +++ .../DataType.cs | 2 +- .../Constants.cs | 2 + .../Contracts/AuxiliarySymbolsFactory.cs | 16 ++ .../Contracts/AuxiliarySymbols_1.cs | 48 ++++++ .../Data/JitHelperInfo.cs | 21 +++ .../ISOSDacInterface.cs | 2 +- .../OutputBufferHelpers.cs | 18 +++ .../SOSDacImpl.cs | 14 +- .../CachingContractRegistry.cs | 1 + .../cdac/tests/AuxiliarySymbolsTests.cs | 137 ++++++++++++++++++ 19 files changed, 387 insertions(+), 6 deletions(-) create mode 100644 docs/design/datacontracts/AuxiliarySymbols.md create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IAuxiliarySymbols.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbolsFactory.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/JitHelperInfo.cs create mode 100644 src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs diff --git a/docs/design/datacontracts/AuxiliarySymbols.md b/docs/design/datacontracts/AuxiliarySymbols.md new file mode 100644 index 00000000000000..729c690331bfa9 --- /dev/null +++ b/docs/design/datacontracts/AuxiliarySymbols.md @@ -0,0 +1,58 @@ +# Contract AuxiliarySymbols + +This contract provides name resolution for JIT helper functions whose executing code +resides at dynamically-determined addresses. + +## APIs of contract + +``` csharp +// Attempts to resolve a code address to a JIT helper function name. +// Returns true if the address matches a known helper, with the name in helperName. +// Returns false if the address does not match any known helper. +bool TryGetJitHelperName(TargetPointer ip, out string helperName); +``` + +## Version 1 + +Data descriptors used: +| Data Descriptor Name | Field | Meaning | +| --- | --- | --- | +| `JitHelperInfo` | `Address` | Code pointer to the dynamically-located helper function | +| `JitHelperInfo` | `Name` | Pointer to a null-terminated wide string with the helper name | + +Global variables used: +| Global Name | Type | Purpose | +| --- | --- | --- | +| `InterestingJitHelpers` | TargetPointer | Pointer to an array of `JitHelperInfo` entries | +| `InterestingJitHelperCount` | TargetPointer | Pointer to the count of populated entries in the array | + +Contracts used: none + +``` csharp +bool TryGetJitHelperName(TargetPointer ip, out string helperName) +{ + helperName = null; + + TargetCodePointer codePointer = CodePointerFromAddress(ip); + + TargetPointer helperArray = target.ReadGlobalPointer("InterestingJitHelpers"); + int count = target.Read(target.ReadGlobalPointer("InterestingJitHelperCount")); + + uint entrySize = /* JitHelperInfo size */; + + for (int i = 0; i < count; i++) + { + TargetPointer entryAddr = helperArray + (i * entrySize); + TargetCodePointer address = target.ReadCodePointer(entryAddr + /* JitHelperInfo::Address offset */); + TargetPointer namePointer = target.ReadPointer(entryAddr + /* JitHelperInfo::Name offset */); + + if (address == codePointer && namePointer != TargetPointer.Null) + { + helperName = target.ReadUtf16String(namePointer); + return true; + } + } + + return false; +} +``` diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 80d4e0d5506cf2..545c553cf75464 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -92,6 +92,8 @@ DEFINE_DACVAR(PTR_InterpreterCodeManager, ExecutionManager__m_pInterpreterCodeMa DEFINE_DACVAR_NO_DUMP(VMHELPDEF *, dac__hlpFuncTable, ::hlpFuncTable) DEFINE_DACVAR(VMHELPDEF *, dac__hlpDynamicFuncTable, ::hlpDynamicFuncTable) +DEFINE_DACVAR(VMINTERESTINGJITHELPDEF *, dac__hlpInterestingJitHelpTable, ::hlpInterestingJitHelpTable) +DEFINE_DACVAR(int, dac__g_interestingJitHelpCount, ::g_interestingJitHelpCount) DEFINE_DACVAR(PTR_StubManager, StubManager__g_pFirstManager, StubManager::g_pFirstManager) DEFINE_DACVAR(PTR_PrecodeStubManager, PrecodeStubManager__g_pManager, PrecodeStubManager::g_pManager) diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index e73483de784ae5..acaf7497dc6fe9 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -1239,6 +1239,12 @@ CDAC_TYPE_FIELD(WebcilSectionHeader, /*uint32*/, PointerToRawData, offsetof(Webc CDAC_TYPE_END(WebcilSectionHeader) #endif +CDAC_TYPE_BEGIN(JitHelperInfo) +CDAC_TYPE_SIZE(sizeof(VMINTERESTINGJITHELPDEF)) +CDAC_TYPE_FIELD(JitHelperInfo, /*pointer*/, Address, offsetof(VMINTERESTINGJITHELPDEF, pfnWriteBarrier)) +CDAC_TYPE_FIELD(JitHelperInfo, /*pointer*/, Name, offsetof(VMINTERESTINGJITHELPDEF, name)) +CDAC_TYPE_END(JitHelperInfo) + CDAC_TYPES_END() CDAC_GLOBALS_BEGIN() @@ -1408,6 +1414,8 @@ CDAC_GLOBAL(SyncBlockHashCodeMask, uint32, MASK_HASHCODE) CDAC_GLOBAL_POINTER(GCLowestAddress, &g_lowest_address) CDAC_GLOBAL_POINTER(GCHighestAddress, &g_highest_address) +CDAC_GLOBAL_POINTER(InterestingJitHelpers, &::hlpInterestingJitHelpTable) +CDAC_GLOBAL_POINTER(InterestingJitHelperCount, &::g_interestingJitHelpCount) #if FEATURE_COMINTEROP CDAC_GLOBAL(CCWNumInterfaces, uint32, cdac_data::NumInterfaces) CDAC_GLOBAL(CCWThisMask, nuint, cdac_data::ThisMask) @@ -1425,6 +1433,7 @@ CDAC_GLOBAL(RCWInterfaceCacheSize, uint32, INTERFACE_ENTRY_CACHE_SIZE) // When adding a new subdescriptor, EnumMemDescriptors must be updated appropriately. CDAC_GLOBAL_SUB_DESCRIPTOR(GC, &(g_gc_dac_vars.gc_descriptor)) +CDAC_GLOBAL_CONTRACT(AuxiliarySymbols, 1) #if FEATURE_COMINTEROP CDAC_GLOBAL_CONTRACT(BuiltInCOM, 1) #endif // FEATURE_COMINTEROP @@ -1432,6 +1441,7 @@ CDAC_GLOBAL_CONTRACT(CodeVersions, 1) #ifdef FEATURE_COMWRAPPERS CDAC_GLOBAL_CONTRACT(ComWrappers, 1) #endif // FEATURE_COMWRAPPERS +CDAC_GLOBAL_CONTRACT(ConditionalWeakTable, 1) CDAC_GLOBAL_CONTRACT(DacStreams, 1) CDAC_GLOBAL_CONTRACT(DebugInfo, 2) CDAC_GLOBAL_CONTRACT(EcmaMetadata, 1) @@ -1448,10 +1458,9 @@ CDAC_GLOBAL_CONTRACT(RuntimeInfo, 1) CDAC_GLOBAL_CONTRACT(RuntimeTypeSystem, 1) CDAC_GLOBAL_CONTRACT(SHash, 1) CDAC_GLOBAL_CONTRACT(SignatureDecoder, 1) -CDAC_GLOBAL_CONTRACT(ConditionalWeakTable, 1) -CDAC_GLOBAL_CONTRACT(SyncBlock, 1) CDAC_GLOBAL_CONTRACT(StackWalk, 1) CDAC_GLOBAL_CONTRACT(StressLog, 2) +CDAC_GLOBAL_CONTRACT(SyncBlock, 1) CDAC_GLOBAL_CONTRACT(Thread, 1) CDAC_GLOBALS_END() diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index c16e2513f9972e..b8082682401da3 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -2506,6 +2506,24 @@ void _SetJitHelperFunction(DynamicCorInfoHelpFunc ftnNum, void * pFunc) hlpDynamicFuncTable[ftnNum].pfnHelper = (PCODE)pFunc; } +VMINTERESTINGJITHELPDEF hlpInterestingJitHelpTable[MAX_INTERESTING_JIT_HELPERS]; +int g_interestingJitHelpCount = 0; + +void SetInterestingJitHelperFunction(void* pFunc, LPCWSTR name) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(g_interestingJitHelpCount < MAX_INTERESTING_JIT_HELPERS); + hlpInterestingJitHelpTable[g_interestingJitHelpCount].pfnWriteBarrier = (PCODE)pFunc; + hlpInterestingJitHelpTable[g_interestingJitHelpCount].name = name; + g_interestingJitHelpCount++; +} + PCODE LoadDynamicJitHelper(DynamicCorInfoHelpFunc ftnNum) { STANDARD_VM_CONTRACT; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index f1af7efa20c416..f5614f1385d8de 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -81,6 +81,8 @@ // Hence, we add them here. GARY_IMPL(VMHELPDEF, hlpFuncTable, CORINFO_HELP_COUNT); GARY_IMPL(VMHELPDEF, hlpDynamicFuncTable, DYNAMIC_CORINFO_HELP_COUNT); +GARY_IMPL(VMINTERESTINGJITHELPDEF, hlpInterestingJitHelpTable, MAX_INTERESTING_JIT_HELPERS); +GVAL_IMPL_INIT(int, g_interestingJitHelpCount, 0); #else // DACCESS_COMPILE diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index ed9d728ee88748..916f5cf761e783 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -1005,13 +1005,25 @@ struct VMHELPDEF bool IsDynamicHelper(DynamicCorInfoHelpFunc* dynamicFtnNum) const; }; +struct VMINTERESTINGJITHELPDEF +{ + PCODE pfnWriteBarrier; + LPCWSTR name; +}; + +#define MAX_INTERESTING_JIT_HELPERS 7 + #if defined(DACCESS_COMPILE) GARY_DECL(VMHELPDEF, hlpFuncTable, CORINFO_HELP_COUNT); +GARY_DECL(VMINTERESTINGJITHELPDEF, hlpInterestingJitHelpTable, MAX_INTERESTING_JIT_HELPERS); +GVAL_DECL(int, g_interestingJitHelpCount); #else extern "C" const VMHELPDEF hlpFuncTable[CORINFO_HELP_COUNT]; +extern "C" VMINTERESTINGJITHELPDEF hlpInterestingJitHelpTable[MAX_INTERESTING_JIT_HELPERS]; +extern "C" int g_interestingJitHelpCount; #ifdef FEATURE_PORTABLE_ENTRYPOINTS extern "C" PCODE hlpFuncEntryPoints[CORINFO_HELP_COUNT]; @@ -1027,6 +1039,7 @@ GARY_DECL(VMHELPDEF, hlpDynamicFuncTable, DYNAMIC_CORINFO_HELP_COUNT); #define SetJitHelperFunction(ftnNum, pFunc) _SetJitHelperFunction(DYNAMIC_##ftnNum, (void*)(pFunc)) void _SetJitHelperFunction(DynamicCorInfoHelpFunc ftnNum, void * pFunc); +void SetInterestingJitHelperFunction(void* pFunc, LPCWSTR name); PCODE LoadDynamicJitHelper(DynamicCorInfoHelpFunc ftnNum); bool HasILBasedDynamicJitHelper(DynamicCorInfoHelpFunc ftnNum); diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index f0cf759e010cbf..f3075da05f60c3 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -1082,6 +1082,7 @@ void InitThreadManager() #define X86_WRITE_BARRIER_REGISTER(reg) \ SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF_##reg, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg)); \ + SetInterestingJitHelperFunction(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg), W("JIT_WriteBarrier" #reg)); \ ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg), W("@WriteBarrier" #reg)); ENUM_X86_WRITE_BARRIER_REGISTERS() @@ -1092,6 +1093,7 @@ void InitThreadManager() JIT_WriteBarrier_Loc = GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier); #endif // TARGET_X86 SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier)); + SetInterestingJitHelperFunction(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), W("JIT_WriteBarrier")); ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), W("@WriteBarrier")); #if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) @@ -1101,8 +1103,10 @@ void InitThreadManager() #if defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) SetJitHelperFunction(CORINFO_HELP_CHECKED_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier)); + SetInterestingJitHelperFunction(GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier), W("JIT_CheckedWriteBarrier")); ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier), W("@CheckedWriteBarrier")); SetJitHelperFunction(CORINFO_HELP_ASSIGN_BYREF, GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier)); + SetInterestingJitHelperFunction(GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier), W("JIT_ByRefWriteBarrier")); ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier), W("@ByRefWriteBarrier")); #endif // TARGET_ARM64 || TARGET_ARM || TARGET_LOONGARCH64 || TARGET_RISCV64 diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs index 7362458b770241..f47c90249aefa5 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs @@ -106,6 +106,10 @@ public abstract class ContractRegistry /// Gets an instance of the ConditionalWeakTable contract for the target. /// public virtual IConditionalWeakTable ConditionalWeakTable => GetContract(); + /// + /// Gets an instance of the AuxiliarySymbols contract for the target. + /// + public virtual IAuxiliarySymbols AuxiliarySymbols => GetContract(); public abstract TContract GetContract() where TContract : IContract; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IAuxiliarySymbols.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IAuxiliarySymbols.cs new file mode 100644 index 00000000000000..8fddb828ebad03 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IAuxiliarySymbols.cs @@ -0,0 +1,18 @@ +// 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.CodeAnalysis; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts; + +public interface IAuxiliarySymbols : IContract +{ + static string IContract.Name { get; } = nameof(AuxiliarySymbols); + bool TryGetJitHelperName(TargetPointer ip, [NotNullWhen(true)] out string? helperName) => throw new NotImplementedException(); +} + +public readonly struct AuxiliarySymbols : IAuxiliarySymbols +{ + // Everything throws NotImplementedException +} 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 89f9f2e5559378..8404f5f6f5d7fc 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -161,7 +161,7 @@ public enum DataType InterfaceEntry, ComInterfaceEntry, InternalComInterfaceDispatch, - + JitHelperInfo, /* GC Data Types */ 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 eafeb8137df884..3999743f0dc389 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -154,6 +154,8 @@ public static class Globals public const string HandlesPerBlock = nameof(HandlesPerBlock); public const string BlockInvalid = nameof(BlockInvalid); public const string TotalCpuCount = nameof(TotalCpuCount); + public const string InterestingJitHelpers = nameof(InterestingJitHelpers); + public const string InterestingJitHelperCount = nameof(InterestingJitHelperCount); } public static class FieldNames { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbolsFactory.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbolsFactory.cs new file mode 100644 index 00000000000000..10faa7c9e7f028 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbolsFactory.cs @@ -0,0 +1,16 @@ +// 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; + +public sealed class AuxiliarySymbolsFactory : IContractFactory +{ + IAuxiliarySymbols IContractFactory.CreateContract(Target target, int version) + { + return version switch + { + 1 => new AuxiliarySymbols_1(target), + _ => default(AuxiliarySymbols), + }; + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs new file mode 100644 index 00000000000000..b631d136cea412 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts; + +internal readonly struct AuxiliarySymbols_1 : IAuxiliarySymbols +{ + private readonly Target _target; + + internal AuxiliarySymbols_1(Target target) + { + _target = target; + } + + bool IAuxiliarySymbols.TryGetJitHelperName(TargetPointer ip, [NotNullWhen(true)] out string? helperName) + { + helperName = null; + + TargetCodePointer codePointer = CodePointerUtils.CodePointerFromAddress(ip, _target); + + TargetPointer helperArrayPtr = _target.ReadGlobalPointer(Constants.Globals.InterestingJitHelpers); + int helperCount = _target.Read(_target.ReadGlobalPointer(Constants.Globals.InterestingJitHelperCount)); + + Target.TypeInfo typeInfo = _target.GetTypeInfo(DataType.JitHelperInfo); + uint entrySize = typeInfo.Size!.Value; + + for (int i = 0; i < helperCount; i++) + { + TargetPointer entryAddr = helperArrayPtr + (ulong)(i * entrySize); + Data.JitHelperInfo entry = _target.ProcessedData.GetOrAdd(entryAddr); + + if (entry.Address == codePointer) + { + if (entry.Name != TargetPointer.Null) + { + helperName = _target.ReadUtf16String(entry.Name); + return true; + } + + return false; + } + } + + return false; + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/JitHelperInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/JitHelperInfo.cs new file mode 100644 index 00000000000000..67b4ec444ce548 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/JitHelperInfo.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 JitHelperInfo : IData +{ + static JitHelperInfo IData.Create(Target target, TargetPointer address) + => new JitHelperInfo(target, address); + + public JitHelperInfo(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.JitHelperInfo); + + Address = target.ReadCodePointer(address + (ulong)type.Fields[nameof(Address)].Offset); + Name = target.ReadPointer(address + (ulong)type.Fields[nameof(Name)].Offset); + } + + public TargetCodePointer Address { get; init; } + public TargetPointer Name { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs index 4fca56fce16a90..99d12a31283eea 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs @@ -691,7 +691,7 @@ public unsafe partial interface ISOSDacInterface [PreserveSig] int GetJitManagerList(uint count, DacpJitManagerInfo* managers, uint* pNeeded); [PreserveSig] - int GetJitHelperFunctionName(ClrDataAddress ip, uint count, byte* name, uint* pNeeded); + int GetJitHelperFunctionName(ClrDataAddress ip, uint count, [In, MarshalUsing(CountElementName = nameof(count)), Out] byte[]? name, uint* pNeeded); [PreserveSig] int GetJumpThunkTarget(/*T_CONTEXT*/void* ctx, ClrDataAddress* targetIP, ClrDataAddress* targetMD); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs index 51ae00d6d1b962..7f03b1a11276d3 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Text; namespace Microsoft.Diagnostics.DataContractReader.Legacy; @@ -22,4 +23,21 @@ public static unsafe void CopyStringToBuffer(char* stringBuf, uint bufferSize, u target[nullTerminatorLocation] = '\0'; } } + + public static unsafe void CopyUtf8StringToBuffer(byte[]? stringBuf, uint bufferSize, uint* neededBufferSize, string str) + { + int byteCount = Encoding.UTF8.GetByteCount(str); + if (neededBufferSize is not null) + *neededBufferSize = (uint)(byteCount + 1); + + if (stringBuf is not null && bufferSize > 0) + { + int maxBytes = Math.Min(byteCount, (int)bufferSize - 1); + fixed (byte* pBuf = stringBuf) + { + Encoding.UTF8.GetBytes(str.AsSpan(), new Span(pBuf, maxBytes)); + pBuf[maxBytes] = 0; + } + } + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs index 33cea4425e5c30..b491a5425fc93b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -1925,8 +1925,18 @@ int ISOSDacInterface.GetILForModule(ClrDataAddress moduleAddr, int rva, ClrDataA #endif return hr; } - 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.GetJitHelperFunctionName(ClrDataAddress ip, uint count, [In, MarshalUsing(CountElementName = nameof(count)), Out] byte[]? name, uint* pNeeded) + { + // There is deliberately no debug validation here because we change behavior + // to only handle the JIT helpers that cannot be handled as unmanaged or managed symbols + // and to provide more informative names. + if (!_target.Contracts.AuxiliarySymbols.TryGetJitHelperName(ip.ToTargetPointer(_target), out string? helperName)) + return HResults.E_FAIL; + + OutputBufferHelpers.CopyUtf8StringToBuffer(name, count, pNeeded, helperName); + + return HResults.S_OK; + } int ISOSDacInterface.GetJitManagerList(uint count, DacpJitManagerInfo* managers, uint* pNeeded) { int hr = HResults.S_OK; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs index bc4766f0e019a5..3a92a27eea6d89 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs @@ -50,6 +50,7 @@ public CachingContractRegistry(Target target, TryGetContractVersionDelegate tryG [typeof(ISyncBlock)] = new SyncBlockFactory(), [typeof(IBuiltInCOM)] = new BuiltInCOMFactory(), [typeof(IConditionalWeakTable)] = new ConditionalWeakTableFactory(), + [typeof(IAuxiliarySymbols)] = new AuxiliarySymbolsFactory(), }; foreach (IContractFactory factory in additionalFactories) diff --git a/src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs b/src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs new file mode 100644 index 00000000000000..fad907790665d6 --- /dev/null +++ b/src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs @@ -0,0 +1,137 @@ +// 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; +using Microsoft.Diagnostics.DataContractReader.Contracts; +using Moq; +using Xunit; + +namespace Microsoft.Diagnostics.DataContractReader.Tests; + +public class AuxiliarySymbolsTests +{ + private static readonly MockDescriptors.TypeFields JitHelperInfoFields = new() + { + DataType = DataType.JitHelperInfo, + Fields = + [ + new(nameof(Data.JitHelperInfo.Address), DataType.pointer), + new(nameof(Data.JitHelperInfo.Name), DataType.pointer), + ] + }; + + private static Target CreateTarget( + MockTarget.Architecture arch, + (ulong Address, string Name)[] helpers) + { + TargetTestHelpers targetTestHelpers = new(arch); + MockMemorySpace.Builder builder = new(targetTestHelpers); + + Dictionary types = + MockDescriptors.GetTypesForTypeFields(targetTestHelpers, [JitHelperInfoFields]); + uint entrySize = types[DataType.JitHelperInfo].Size!.Value; + + MockMemorySpace.BumpAllocator allocator = builder.CreateAllocator(0x1000_0000, 0x2000_0000); + + // Allocate the array + MockMemorySpace.HeapFragment arrayFragment = allocator.Allocate(entrySize * (ulong)helpers.Length, "JitHelperInfoArray"); + + // Write each entry + Target.TypeInfo typeInfo = types[DataType.JitHelperInfo]; + for (int i = 0; i < helpers.Length; i++) + { + int addressOffset = typeInfo.Fields[nameof(Data.JitHelperInfo.Address)].Offset; + int nameOffset = typeInfo.Fields[nameof(Data.JitHelperInfo.Name)].Offset; + Span entryData = arrayFragment.Data.AsSpan((int)(i * entrySize), (int)entrySize); + + // Write the code pointer address + targetTestHelpers.WritePointer(entryData.Slice(addressOffset), helpers[i].Address); + + // Allocate and write the UTF-16 name string + byte[] nameBytes = (arch.IsLittleEndian ? Encoding.Unicode : Encoding.BigEndianUnicode).GetBytes(helpers[i].Name + '\0'); + MockMemorySpace.HeapFragment nameFragment = allocator.Allocate((ulong)nameBytes.Length, $"Name_{helpers[i].Name}"); + nameBytes.CopyTo(nameFragment.Data.AsSpan()); + builder.AddHeapFragment(nameFragment); + + targetTestHelpers.WritePointer(entryData.Slice(nameOffset), nameFragment.Address); + } + builder.AddHeapFragment(arrayFragment); + + // Allocate global for the count + MockMemorySpace.HeapFragment countFragment = allocator.Allocate(sizeof(int), "HelperCount"); + targetTestHelpers.Write(countFragment.Data, helpers.Length); + builder.AddHeapFragment(countFragment); + + (string Name, ulong Value)[] globals = + [ + (Constants.Globals.InterestingJitHelpers, arrayFragment.Address), + (Constants.Globals.InterestingJitHelperCount, countFragment.Address), + ]; + + var target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, types, globals); + + Mock platformMetadata = new(); + platformMetadata.Setup(p => p.GetCodePointerFlags()).Returns(default(CodePointerFlags)); + + IContractFactory factory = new AuxiliarySymbolsFactory(); + Mock reg = new(); + reg.SetupGet(c => c.PlatformMetadata).Returns(platformMetadata.Object); + reg.SetupGet(c => c.AuxiliarySymbols).Returns(() => factory.CreateContract(target, 1)); + target.SetContracts(reg.Object); + + return target; + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void TryGetJitHelperName_MatchFound_ReturnsTrue(MockTarget.Architecture arch) + { + ulong writeBarrierAddr = 0x7FFF_0100; + ulong checkedBarrierAddr = 0x7FFF_0200; + + var target = CreateTarget(arch, + [ + (writeBarrierAddr, "@WriteBarrier"), + (checkedBarrierAddr, "@CheckedWriteBarrier"), + ]); + + bool found = target.Contracts.AuxiliarySymbols.TryGetJitHelperName( + new TargetPointer(writeBarrierAddr), out string? name); + + Assert.True(found); + Assert.Equal("@WriteBarrier", name); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void TryGetJitHelperName_NoMatch_ReturnsFalse(MockTarget.Architecture arch) + { + ulong writeBarrierAddr = 0x7FFF_0100; + + var target = CreateTarget(arch, + [ + (writeBarrierAddr, "@WriteBarrier"), + ]); + + bool found = target.Contracts.AuxiliarySymbols.TryGetJitHelperName( + new TargetPointer(0xDEAD_BEEF), out string? name); + + Assert.False(found); + Assert.Null(name); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void TryGetJitHelperName_EmptyArray_ReturnsFalse(MockTarget.Architecture arch) + { + var target = CreateTarget(arch, []); + + bool found = target.Contracts.AuxiliarySymbols.TryGetJitHelperName( + new TargetPointer(0x7FFF_0100), out string? name); + + Assert.False(found); + Assert.Null(name); + } +} From f1c41a7bf7cfc8bc9a990049572da7ab83636df6 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Mon, 30 Mar 2026 11:37:20 -0700 Subject: [PATCH 2/4] code review --- docs/design/datacontracts/AuxiliarySymbols.md | 32 ++++---- src/coreclr/inc/dacvars.h | 4 +- .../vm/datadescriptor/datadescriptor.inc | 14 ++-- src/coreclr/vm/jithelpers.cpp | 14 ++-- src/coreclr/vm/jitinterface.cpp | 4 +- src/coreclr/vm/jitinterface.h | 18 ++--- src/coreclr/vm/threads.cpp | 8 +- .../Contracts/IAuxiliarySymbols.cs | 2 +- .../DataType.cs | 2 +- .../Constants.cs | 4 +- .../Contracts/AuxiliarySymbols_1.cs | 14 ++-- .../Data/JitHelperInfo.cs | 10 +-- .../ISOSDacInterface.cs | 2 +- .../OutputBufferHelpers.cs | 10 +-- .../SOSDacImpl.cs | 23 ++++-- .../cdac/tests/AuxiliarySymbolsTests.cs | 75 ++++++++++--------- 16 files changed, 126 insertions(+), 110 deletions(-) diff --git a/docs/design/datacontracts/AuxiliarySymbols.md b/docs/design/datacontracts/AuxiliarySymbols.md index 729c690331bfa9..d1974a48e7e380 100644 --- a/docs/design/datacontracts/AuxiliarySymbols.md +++ b/docs/design/datacontracts/AuxiliarySymbols.md @@ -1,15 +1,15 @@ # Contract AuxiliarySymbols -This contract provides name resolution for JIT helper functions whose executing code +This contract provides name resolution for helper functions whose executing code resides at dynamically-determined addresses. ## APIs of contract ``` csharp -// Attempts to resolve a code address to a JIT helper function name. -// Returns true if the address matches a known helper, with the name in helperName. +// Attempts to resolve a code address to a helper function name. +// Returns true if the address matches a known helper, with the name in symbolName. // Returns false if the address does not match any known helper. -bool TryGetJitHelperName(TargetPointer ip, out string helperName); +bool TryGetAuxiliarySymbolName(TargetPointer ip, out string symbolName); ``` ## Version 1 @@ -17,38 +17,38 @@ bool TryGetJitHelperName(TargetPointer ip, out string helperName); Data descriptors used: | Data Descriptor Name | Field | Meaning | | --- | --- | --- | -| `JitHelperInfo` | `Address` | Code pointer to the dynamically-located helper function | -| `JitHelperInfo` | `Name` | Pointer to a null-terminated wide string with the helper name | +| `AuxiliarySymbolInfo` | `Address` | Code pointer to the dynamically-located helper function | +| `AuxiliarySymbolInfo` | `Name` | Pointer to a null-terminated wide string with the helper name | Global variables used: | Global Name | Type | Purpose | | --- | --- | --- | -| `InterestingJitHelpers` | TargetPointer | Pointer to an array of `JitHelperInfo` entries | -| `InterestingJitHelperCount` | TargetPointer | Pointer to the count of populated entries in the array | +| `AuxiliarySymbols` | TargetPointer | Pointer to an array of `AuxiliarySymbolInfo` entries | +| `AuxiliarySymbolCount` | TargetPointer | Pointer to the count of populated entries in the array | Contracts used: none ``` csharp -bool TryGetJitHelperName(TargetPointer ip, out string helperName) +bool TryGetAuxiliarySymbolName(TargetPointer ip, out string? symbolName) { - helperName = null; + symbolName = null; TargetCodePointer codePointer = CodePointerFromAddress(ip); - TargetPointer helperArray = target.ReadGlobalPointer("InterestingJitHelpers"); - int count = target.Read(target.ReadGlobalPointer("InterestingJitHelperCount")); + TargetPointer helperArray = target.ReadGlobalPointer("AuxiliarySymbols"); + int count = target.Read(target.ReadGlobalPointer("AuxiliarySymbolCount")); - uint entrySize = /* JitHelperInfo size */; + uint entrySize = /* AuxiliarySymbolInfo size */; for (int i = 0; i < count; i++) { TargetPointer entryAddr = helperArray + (i * entrySize); - TargetCodePointer address = target.ReadCodePointer(entryAddr + /* JitHelperInfo::Address offset */); - TargetPointer namePointer = target.ReadPointer(entryAddr + /* JitHelperInfo::Name offset */); + TargetCodePointer address = target.ReadCodePointer(entryAddr + /* AuxiliarySymbolInfo::Address offset */); + TargetPointer namePointer = target.ReadPointer(entryAddr + /* AuxiliarySymbolInfo::Name offset */); if (address == codePointer && namePointer != TargetPointer.Null) { - helperName = target.ReadUtf16String(namePointer); + symbolName = target.ReadUtf16String(namePointer); return true; } } diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 545c553cf75464..37b34c621cc76a 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -92,8 +92,8 @@ DEFINE_DACVAR(PTR_InterpreterCodeManager, ExecutionManager__m_pInterpreterCodeMa DEFINE_DACVAR_NO_DUMP(VMHELPDEF *, dac__hlpFuncTable, ::hlpFuncTable) DEFINE_DACVAR(VMHELPDEF *, dac__hlpDynamicFuncTable, ::hlpDynamicFuncTable) -DEFINE_DACVAR(VMINTERESTINGJITHELPDEF *, dac__hlpInterestingJitHelpTable, ::hlpInterestingJitHelpTable) -DEFINE_DACVAR(int, dac__g_interestingJitHelpCount, ::g_interestingJitHelpCount) +DEFINE_DACVAR(VMAUXILIARYSYMBOLDEF *, dac__hlpAuxiliarySymbolTable, ::hlpAuxiliarySymbolTable) +DEFINE_DACVAR(int, dac__g_auxiliarySymbolCount, ::g_auxiliarySymbolCount) DEFINE_DACVAR(PTR_StubManager, StubManager__g_pFirstManager, StubManager::g_pFirstManager) DEFINE_DACVAR(PTR_PrecodeStubManager, PrecodeStubManager__g_pManager, PrecodeStubManager::g_pManager) diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index acaf7497dc6fe9..4d8294e900ebdd 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -1239,11 +1239,11 @@ CDAC_TYPE_FIELD(WebcilSectionHeader, /*uint32*/, PointerToRawData, offsetof(Webc CDAC_TYPE_END(WebcilSectionHeader) #endif -CDAC_TYPE_BEGIN(JitHelperInfo) -CDAC_TYPE_SIZE(sizeof(VMINTERESTINGJITHELPDEF)) -CDAC_TYPE_FIELD(JitHelperInfo, /*pointer*/, Address, offsetof(VMINTERESTINGJITHELPDEF, pfnWriteBarrier)) -CDAC_TYPE_FIELD(JitHelperInfo, /*pointer*/, Name, offsetof(VMINTERESTINGJITHELPDEF, name)) -CDAC_TYPE_END(JitHelperInfo) +CDAC_TYPE_BEGIN(AuxiliarySymbolInfo) +CDAC_TYPE_SIZE(sizeof(VMAUXILIARYSYMBOLDEF)) +CDAC_TYPE_FIELD(AuxiliarySymbolInfo, /*pointer*/, Address, offsetof(VMAUXILIARYSYMBOLDEF, pfnAuxiliarySymbol)) +CDAC_TYPE_FIELD(AuxiliarySymbolInfo, /*pointer*/, Name, offsetof(VMAUXILIARYSYMBOLDEF, name)) +CDAC_TYPE_END(AuxiliarySymbolInfo) CDAC_TYPES_END() @@ -1414,8 +1414,8 @@ CDAC_GLOBAL(SyncBlockHashCodeMask, uint32, MASK_HASHCODE) CDAC_GLOBAL_POINTER(GCLowestAddress, &g_lowest_address) CDAC_GLOBAL_POINTER(GCHighestAddress, &g_highest_address) -CDAC_GLOBAL_POINTER(InterestingJitHelpers, &::hlpInterestingJitHelpTable) -CDAC_GLOBAL_POINTER(InterestingJitHelperCount, &::g_interestingJitHelpCount) +CDAC_GLOBAL_POINTER(AuxiliarySymbols, &::hlpAuxiliarySymbolTable) +CDAC_GLOBAL_POINTER(AuxiliarySymbolCount, &::g_auxiliarySymbolCount) #if FEATURE_COMINTEROP CDAC_GLOBAL(CCWNumInterfaces, uint32, cdac_data::NumInterfaces) CDAC_GLOBAL(CCWThisMask, nuint, cdac_data::ThisMask) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index b8082682401da3..612c418f05bdb0 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -2506,10 +2506,10 @@ void _SetJitHelperFunction(DynamicCorInfoHelpFunc ftnNum, void * pFunc) hlpDynamicFuncTable[ftnNum].pfnHelper = (PCODE)pFunc; } -VMINTERESTINGJITHELPDEF hlpInterestingJitHelpTable[MAX_INTERESTING_JIT_HELPERS]; -int g_interestingJitHelpCount = 0; +VMAUXILIARYSYMBOLDEF hlpAuxiliarySymbolTable[MAX_AUXILIARY_SYMBOLS]; +int g_auxiliarySymbolCount = 0; -void SetInterestingJitHelperFunction(void* pFunc, LPCWSTR name) +void SetAuxiliarySymbol(void* pFunc, const char* name) { CONTRACTL { @@ -2518,10 +2518,10 @@ void SetInterestingJitHelperFunction(void* pFunc, LPCWSTR name) } CONTRACTL_END; - _ASSERTE(g_interestingJitHelpCount < MAX_INTERESTING_JIT_HELPERS); - hlpInterestingJitHelpTable[g_interestingJitHelpCount].pfnWriteBarrier = (PCODE)pFunc; - hlpInterestingJitHelpTable[g_interestingJitHelpCount].name = name; - g_interestingJitHelpCount++; + _ASSERTE(g_auxiliarySymbolCount < MAX_AUXILIARY_SYMBOLS); + hlpAuxiliarySymbolTable[g_auxiliarySymbolCount].pfnAuxiliarySymbol = (PCODE)pFunc; + hlpAuxiliarySymbolTable[g_auxiliarySymbolCount].name = name; + g_auxiliarySymbolCount++; } PCODE LoadDynamicJitHelper(DynamicCorInfoHelpFunc ftnNum) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index f5614f1385d8de..b5229e2444c101 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -81,8 +81,8 @@ // Hence, we add them here. GARY_IMPL(VMHELPDEF, hlpFuncTable, CORINFO_HELP_COUNT); GARY_IMPL(VMHELPDEF, hlpDynamicFuncTable, DYNAMIC_CORINFO_HELP_COUNT); -GARY_IMPL(VMINTERESTINGJITHELPDEF, hlpInterestingJitHelpTable, MAX_INTERESTING_JIT_HELPERS); -GVAL_IMPL_INIT(int, g_interestingJitHelpCount, 0); +GARY_IMPL(VMAUXILIARYSYMBOLDEF, hlpAuxiliarySymbolTable, MAX_AUXILIARY_SYMBOLS); +GVAL_IMPL_INIT(int, g_auxiliarySymbolCount, 0); #else // DACCESS_COMPILE diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 916f5cf761e783..7db6d694da7599 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -1005,25 +1005,25 @@ struct VMHELPDEF bool IsDynamicHelper(DynamicCorInfoHelpFunc* dynamicFtnNum) const; }; -struct VMINTERESTINGJITHELPDEF +struct VMAUXILIARYSYMBOLDEF { - PCODE pfnWriteBarrier; - LPCWSTR name; + PCODE pfnAuxiliarySymbol; + const char* name; }; -#define MAX_INTERESTING_JIT_HELPERS 7 +#define MAX_AUXILIARY_SYMBOLS 7 #if defined(DACCESS_COMPILE) GARY_DECL(VMHELPDEF, hlpFuncTable, CORINFO_HELP_COUNT); -GARY_DECL(VMINTERESTINGJITHELPDEF, hlpInterestingJitHelpTable, MAX_INTERESTING_JIT_HELPERS); -GVAL_DECL(int, g_interestingJitHelpCount); +GARY_DECL(VMAUXILIARYSYMBOLDEF, hlpAuxiliarySymbolTable, MAX_AUXILIARY_SYMBOLS); +GVAL_DECL(int, g_auxiliarySymbolCount); #else extern "C" const VMHELPDEF hlpFuncTable[CORINFO_HELP_COUNT]; -extern "C" VMINTERESTINGJITHELPDEF hlpInterestingJitHelpTable[MAX_INTERESTING_JIT_HELPERS]; -extern "C" int g_interestingJitHelpCount; +extern "C" VMAUXILIARYSYMBOLDEF hlpAuxiliarySymbolTable[MAX_AUXILIARY_SYMBOLS]; +extern "C" int g_auxiliarySymbolCount; #ifdef FEATURE_PORTABLE_ENTRYPOINTS extern "C" PCODE hlpFuncEntryPoints[CORINFO_HELP_COUNT]; @@ -1039,7 +1039,7 @@ GARY_DECL(VMHELPDEF, hlpDynamicFuncTable, DYNAMIC_CORINFO_HELP_COUNT); #define SetJitHelperFunction(ftnNum, pFunc) _SetJitHelperFunction(DYNAMIC_##ftnNum, (void*)(pFunc)) void _SetJitHelperFunction(DynamicCorInfoHelpFunc ftnNum, void * pFunc); -void SetInterestingJitHelperFunction(void* pFunc, LPCWSTR name); +void SetAuxiliarySymbol(void* pFunc, const char* name); PCODE LoadDynamicJitHelper(DynamicCorInfoHelpFunc ftnNum); bool HasILBasedDynamicJitHelper(DynamicCorInfoHelpFunc ftnNum); diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index f3075da05f60c3..ee497a17b0bfe2 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -1082,7 +1082,7 @@ void InitThreadManager() #define X86_WRITE_BARRIER_REGISTER(reg) \ SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF_##reg, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg)); \ - SetInterestingJitHelperFunction(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg), W("JIT_WriteBarrier" #reg)); \ + SetAuxiliarySymbol(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg), "JIT_WriteBarrier" #reg); \ ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier##reg), W("@WriteBarrier" #reg)); ENUM_X86_WRITE_BARRIER_REGISTERS() @@ -1093,7 +1093,7 @@ void InitThreadManager() JIT_WriteBarrier_Loc = GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier); #endif // TARGET_X86 SetJitHelperFunction(CORINFO_HELP_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier)); - SetInterestingJitHelperFunction(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), W("JIT_WriteBarrier")); + SetAuxiliarySymbol(GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), "JIT_WriteBarrier"); ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_WriteBarrier), W("@WriteBarrier")); #if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) @@ -1103,10 +1103,10 @@ void InitThreadManager() #if defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) SetJitHelperFunction(CORINFO_HELP_CHECKED_ASSIGN_REF, GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier)); - SetInterestingJitHelperFunction(GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier), W("JIT_CheckedWriteBarrier")); + SetAuxiliarySymbol(GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier), "JIT_CheckedWriteBarrier"); ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_CheckedWriteBarrier), W("@CheckedWriteBarrier")); SetJitHelperFunction(CORINFO_HELP_ASSIGN_BYREF, GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier)); - SetInterestingJitHelperFunction(GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier), W("JIT_ByRefWriteBarrier")); + SetAuxiliarySymbol(GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier), "JIT_ByRefWriteBarrier"); ETW::MethodLog::StubInitialized((ULONGLONG)GetWriteBarrierCodeLocation((void*)JIT_ByRefWriteBarrier), W("@ByRefWriteBarrier")); #endif // TARGET_ARM64 || TARGET_ARM || TARGET_LOONGARCH64 || TARGET_RISCV64 diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IAuxiliarySymbols.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IAuxiliarySymbols.cs index 8fddb828ebad03..3f76e84606d0ee 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IAuxiliarySymbols.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IAuxiliarySymbols.cs @@ -9,7 +9,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; public interface IAuxiliarySymbols : IContract { static string IContract.Name { get; } = nameof(AuxiliarySymbols); - bool TryGetJitHelperName(TargetPointer ip, [NotNullWhen(true)] out string? helperName) => throw new NotImplementedException(); + bool TryGetAuxiliarySymbolName(TargetPointer ip, [NotNullWhen(true)] out string? symbolName) => throw new NotImplementedException(); } public readonly struct AuxiliarySymbols : IAuxiliarySymbols 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 8404f5f6f5d7fc..a27c0434c6f456 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -161,7 +161,7 @@ public enum DataType InterfaceEntry, ComInterfaceEntry, InternalComInterfaceDispatch, - JitHelperInfo, + AuxiliarySymbolInfo, /* GC Data Types */ 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 3999743f0dc389..3241b7ad9217c4 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -154,8 +154,8 @@ public static class Globals public const string HandlesPerBlock = nameof(HandlesPerBlock); public const string BlockInvalid = nameof(BlockInvalid); public const string TotalCpuCount = nameof(TotalCpuCount); - public const string InterestingJitHelpers = nameof(InterestingJitHelpers); - public const string InterestingJitHelperCount = nameof(InterestingJitHelperCount); + public const string AuxiliarySymbols = nameof(AuxiliarySymbols); + public const string AuxiliarySymbolCount = nameof(AuxiliarySymbolCount); } public static class FieldNames { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs index b631d136cea412..9af585a4ef5d26 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs @@ -14,28 +14,28 @@ internal AuxiliarySymbols_1(Target target) _target = target; } - bool IAuxiliarySymbols.TryGetJitHelperName(TargetPointer ip, [NotNullWhen(true)] out string? helperName) + bool IAuxiliarySymbols.TryGetAuxiliarySymbolName(TargetPointer ip, [NotNullWhen(true)] out string? symbolName) { - helperName = null; + symbolName = null; TargetCodePointer codePointer = CodePointerUtils.CodePointerFromAddress(ip, _target); - TargetPointer helperArrayPtr = _target.ReadGlobalPointer(Constants.Globals.InterestingJitHelpers); - int helperCount = _target.Read(_target.ReadGlobalPointer(Constants.Globals.InterestingJitHelperCount)); + TargetPointer helperArrayPtr = _target.ReadGlobalPointer(Constants.Globals.AuxiliarySymbols); + int helperCount = _target.Read(_target.ReadGlobalPointer(Constants.Globals.AuxiliarySymbolCount)); - Target.TypeInfo typeInfo = _target.GetTypeInfo(DataType.JitHelperInfo); + Target.TypeInfo typeInfo = _target.GetTypeInfo(DataType.AuxiliarySymbolInfo); uint entrySize = typeInfo.Size!.Value; for (int i = 0; i < helperCount; i++) { TargetPointer entryAddr = helperArrayPtr + (ulong)(i * entrySize); - Data.JitHelperInfo entry = _target.ProcessedData.GetOrAdd(entryAddr); + Data.AuxiliarySymbolInfo entry = _target.ProcessedData.GetOrAdd(entryAddr); if (entry.Address == codePointer) { if (entry.Name != TargetPointer.Null) { - helperName = _target.ReadUtf16String(entry.Name); + symbolName = _target.ReadUtf8String(entry.Name); return true; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/JitHelperInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/JitHelperInfo.cs index 67b4ec444ce548..77f0fb9054d499 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/JitHelperInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/JitHelperInfo.cs @@ -3,14 +3,14 @@ namespace Microsoft.Diagnostics.DataContractReader.Data; -internal sealed class JitHelperInfo : IData +internal sealed class AuxiliarySymbolInfo : IData { - static JitHelperInfo IData.Create(Target target, TargetPointer address) - => new JitHelperInfo(target, address); + static AuxiliarySymbolInfo IData.Create(Target target, TargetPointer address) + => new AuxiliarySymbolInfo(target, address); - public JitHelperInfo(Target target, TargetPointer address) + public AuxiliarySymbolInfo(Target target, TargetPointer address) { - Target.TypeInfo type = target.GetTypeInfo(DataType.JitHelperInfo); + Target.TypeInfo type = target.GetTypeInfo(DataType.AuxiliarySymbolInfo); Address = target.ReadCodePointer(address + (ulong)type.Fields[nameof(Address)].Offset); Name = target.ReadPointer(address + (ulong)type.Fields[nameof(Name)].Offset); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs index 99d12a31283eea..4fca56fce16a90 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs @@ -691,7 +691,7 @@ public unsafe partial interface ISOSDacInterface [PreserveSig] int GetJitManagerList(uint count, DacpJitManagerInfo* managers, uint* pNeeded); [PreserveSig] - int GetJitHelperFunctionName(ClrDataAddress ip, uint count, [In, MarshalUsing(CountElementName = nameof(count)), Out] byte[]? name, uint* pNeeded); + int GetJitHelperFunctionName(ClrDataAddress ip, uint count, byte* name, uint* pNeeded); [PreserveSig] int GetJumpThunkTarget(/*T_CONTEXT*/void* ctx, ClrDataAddress* targetIP, ClrDataAddress* targetMD); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs index 7f03b1a11276d3..1bda95b27c349d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs @@ -24,7 +24,7 @@ public static unsafe void CopyStringToBuffer(char* stringBuf, uint bufferSize, u } } - public static unsafe void CopyUtf8StringToBuffer(byte[]? stringBuf, uint bufferSize, uint* neededBufferSize, string str) + public static unsafe void CopyUtf8StringToBuffer(byte* stringBuf, uint bufferSize, uint* neededBufferSize, string str) { int byteCount = Encoding.UTF8.GetByteCount(str); if (neededBufferSize is not null) @@ -33,11 +33,9 @@ public static unsafe void CopyUtf8StringToBuffer(byte[]? stringBuf, uint bufferS if (stringBuf is not null && bufferSize > 0) { int maxBytes = Math.Min(byteCount, (int)bufferSize - 1); - fixed (byte* pBuf = stringBuf) - { - Encoding.UTF8.GetBytes(str.AsSpan(), new Span(pBuf, maxBytes)); - pBuf[maxBytes] = 0; - } + Span target = new Span(stringBuf, checked(maxBytes)); + Encoding.UTF8.GetEncoder().Convert(str.AsSpan(), target, true, out _, out int bytesWritten, out _); + stringBuf[bytesWritten] = (byte)'\0'; } } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs index b491a5425fc93b..16e23f1e5d5bc7 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -1925,17 +1925,30 @@ int ISOSDacInterface.GetILForModule(ClrDataAddress moduleAddr, int rva, ClrDataA #endif return hr; } - int ISOSDacInterface.GetJitHelperFunctionName(ClrDataAddress ip, uint count, [In, MarshalUsing(CountElementName = nameof(count)), Out] byte[]? name, uint* pNeeded) + int ISOSDacInterface.GetJitHelperFunctionName(ClrDataAddress ip, uint count, byte* name, uint* pNeeded) { // There is deliberately no debug validation here because we change behavior // to only handle the JIT helpers that cannot be handled as unmanaged or managed symbols // and to provide more informative names. - if (!_target.Contracts.AuxiliarySymbols.TryGetJitHelperName(ip.ToTargetPointer(_target), out string? helperName)) - return HResults.E_FAIL; + int hr = HResults.S_OK; + try + { + if (!_target.Contracts.AuxiliarySymbols.TryGetAuxiliarySymbolName(ip.ToTargetPointer(_target), out string? symbolName)) + throw new ArgumentException(); - OutputBufferHelpers.CopyUtf8StringToBuffer(name, count, pNeeded, helperName); + uint needed = 0; + OutputBufferHelpers.CopyUtf8StringToBuffer(name, count, &needed, symbolName); + if (needed > count) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + if (pNeeded != null) + *pNeeded = needed; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } - return HResults.S_OK; + return hr; } int ISOSDacInterface.GetJitManagerList(uint count, DacpJitManagerInfo* managers, uint* pNeeded) { diff --git a/src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs b/src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs index fad907790665d6..2619483bcf19b1 100644 --- a/src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs +++ b/src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs @@ -12,13 +12,13 @@ namespace Microsoft.Diagnostics.DataContractReader.Tests; public class AuxiliarySymbolsTests { - private static readonly MockDescriptors.TypeFields JitHelperInfoFields = new() + private static readonly MockDescriptors.TypeFields AuxiliarySymbolInfoFields = new() { - DataType = DataType.JitHelperInfo, + DataType = DataType.AuxiliarySymbolInfo, Fields = [ - new(nameof(Data.JitHelperInfo.Address), DataType.pointer), - new(nameof(Data.JitHelperInfo.Name), DataType.pointer), + new(nameof(Data.AuxiliarySymbolInfo.Address), DataType.pointer), + new(nameof(Data.AuxiliarySymbolInfo.Name), DataType.pointer), ] }; @@ -30,34 +30,39 @@ private static Target CreateTarget( MockMemorySpace.Builder builder = new(targetTestHelpers); Dictionary types = - MockDescriptors.GetTypesForTypeFields(targetTestHelpers, [JitHelperInfoFields]); - uint entrySize = types[DataType.JitHelperInfo].Size!.Value; + MockDescriptors.GetTypesForTypeFields(targetTestHelpers, [AuxiliarySymbolInfoFields]); + uint entrySize = types[DataType.AuxiliarySymbolInfo].Size!.Value; MockMemorySpace.BumpAllocator allocator = builder.CreateAllocator(0x1000_0000, 0x2000_0000); - // Allocate the array - MockMemorySpace.HeapFragment arrayFragment = allocator.Allocate(entrySize * (ulong)helpers.Length, "JitHelperInfoArray"); - - // Write each entry - Target.TypeInfo typeInfo = types[DataType.JitHelperInfo]; - for (int i = 0; i < helpers.Length; i++) + // Allocate the array (only if non-empty) + ulong arrayAddress = 0; + if (helpers.Length > 0) { - int addressOffset = typeInfo.Fields[nameof(Data.JitHelperInfo.Address)].Offset; - int nameOffset = typeInfo.Fields[nameof(Data.JitHelperInfo.Name)].Offset; - Span entryData = arrayFragment.Data.AsSpan((int)(i * entrySize), (int)entrySize); - - // Write the code pointer address - targetTestHelpers.WritePointer(entryData.Slice(addressOffset), helpers[i].Address); - - // Allocate and write the UTF-16 name string - byte[] nameBytes = (arch.IsLittleEndian ? Encoding.Unicode : Encoding.BigEndianUnicode).GetBytes(helpers[i].Name + '\0'); - MockMemorySpace.HeapFragment nameFragment = allocator.Allocate((ulong)nameBytes.Length, $"Name_{helpers[i].Name}"); - nameBytes.CopyTo(nameFragment.Data.AsSpan()); - builder.AddHeapFragment(nameFragment); - - targetTestHelpers.WritePointer(entryData.Slice(nameOffset), nameFragment.Address); + MockMemorySpace.HeapFragment arrayFragment = allocator.Allocate(entrySize * (ulong)helpers.Length, "AuxiliarySymbolInfoArray"); + + // Write each entry + Target.TypeInfo typeInfo = types[DataType.AuxiliarySymbolInfo]; + for (int i = 0; i < helpers.Length; i++) + { + int addressOffset = typeInfo.Fields[nameof(Data.AuxiliarySymbolInfo.Address)].Offset; + int nameOffset = typeInfo.Fields[nameof(Data.AuxiliarySymbolInfo.Name)].Offset; + Span entryData = arrayFragment.Data.AsSpan((int)(i * entrySize), (int)entrySize); + + // Write the code pointer address + targetTestHelpers.WritePointer(entryData.Slice(addressOffset), helpers[i].Address); + + // Allocate and write the UTF-8 name string + byte[] nameBytes = Encoding.UTF8.GetBytes(helpers[i].Name + '\0'); + MockMemorySpace.HeapFragment nameFragment = allocator.Allocate((ulong)nameBytes.Length, $"Name_{helpers[i].Name}"); + nameBytes.CopyTo(nameFragment.Data.AsSpan()); + builder.AddHeapFragment(nameFragment); + + targetTestHelpers.WritePointer(entryData.Slice(nameOffset), nameFragment.Address); + } + builder.AddHeapFragment(arrayFragment); + arrayAddress = arrayFragment.Address; } - builder.AddHeapFragment(arrayFragment); // Allocate global for the count MockMemorySpace.HeapFragment countFragment = allocator.Allocate(sizeof(int), "HelperCount"); @@ -66,8 +71,8 @@ private static Target CreateTarget( (string Name, ulong Value)[] globals = [ - (Constants.Globals.InterestingJitHelpers, arrayFragment.Address), - (Constants.Globals.InterestingJitHelperCount, countFragment.Address), + (Constants.Globals.AuxiliarySymbols, arrayAddress), + (Constants.Globals.AuxiliarySymbolCount, countFragment.Address), ]; var target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, types, globals); @@ -86,7 +91,7 @@ private static Target CreateTarget( [Theory] [ClassData(typeof(MockTarget.StdArch))] - public void TryGetJitHelperName_MatchFound_ReturnsTrue(MockTarget.Architecture arch) + public void TryGetAuxiliarySymbolName_MatchFound_ReturnsTrue(MockTarget.Architecture arch) { ulong writeBarrierAddr = 0x7FFF_0100; ulong checkedBarrierAddr = 0x7FFF_0200; @@ -97,7 +102,7 @@ public void TryGetJitHelperName_MatchFound_ReturnsTrue(MockTarget.Architecture a (checkedBarrierAddr, "@CheckedWriteBarrier"), ]); - bool found = target.Contracts.AuxiliarySymbols.TryGetJitHelperName( + bool found = target.Contracts.AuxiliarySymbols.TryGetAuxiliarySymbolName( new TargetPointer(writeBarrierAddr), out string? name); Assert.True(found); @@ -106,7 +111,7 @@ public void TryGetJitHelperName_MatchFound_ReturnsTrue(MockTarget.Architecture a [Theory] [ClassData(typeof(MockTarget.StdArch))] - public void TryGetJitHelperName_NoMatch_ReturnsFalse(MockTarget.Architecture arch) + public void TryGetAuxiliarySymbolName_NoMatch_ReturnsFalse(MockTarget.Architecture arch) { ulong writeBarrierAddr = 0x7FFF_0100; @@ -115,7 +120,7 @@ public void TryGetJitHelperName_NoMatch_ReturnsFalse(MockTarget.Architecture arc (writeBarrierAddr, "@WriteBarrier"), ]); - bool found = target.Contracts.AuxiliarySymbols.TryGetJitHelperName( + bool found = target.Contracts.AuxiliarySymbols.TryGetAuxiliarySymbolName( new TargetPointer(0xDEAD_BEEF), out string? name); Assert.False(found); @@ -124,11 +129,11 @@ public void TryGetJitHelperName_NoMatch_ReturnsFalse(MockTarget.Architecture arc [Theory] [ClassData(typeof(MockTarget.StdArch))] - public void TryGetJitHelperName_EmptyArray_ReturnsFalse(MockTarget.Architecture arch) + public void TryGetAuxiliarySymbolName_EmptyArray_ReturnsFalse(MockTarget.Architecture arch) { var target = CreateTarget(arch, []); - bool found = target.Contracts.AuxiliarySymbols.TryGetJitHelperName( + bool found = target.Contracts.AuxiliarySymbols.TryGetAuxiliarySymbolName( new TargetPointer(0x7FFF_0100), out string? name); Assert.False(found); From e1e58786bf451df9c4be1ee02475994cfd661763 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Mon, 30 Mar 2026 12:57:56 -0700 Subject: [PATCH 3/4] copilot code review --- docs/design/datacontracts/AuxiliarySymbols.md | 4 ++-- .../Data/{JitHelperInfo.cs => AuxiliarySymbolInfo.cs} | 0 .../OutputBufferHelpers.cs | 2 +- .../SOSDacImpl.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/{JitHelperInfo.cs => AuxiliarySymbolInfo.cs} (100%) diff --git a/docs/design/datacontracts/AuxiliarySymbols.md b/docs/design/datacontracts/AuxiliarySymbols.md index d1974a48e7e380..3849a6674f5f4b 100644 --- a/docs/design/datacontracts/AuxiliarySymbols.md +++ b/docs/design/datacontracts/AuxiliarySymbols.md @@ -18,7 +18,7 @@ Data descriptors used: | Data Descriptor Name | Field | Meaning | | --- | --- | --- | | `AuxiliarySymbolInfo` | `Address` | Code pointer to the dynamically-located helper function | -| `AuxiliarySymbolInfo` | `Name` | Pointer to a null-terminated wide string with the helper name | +| `AuxiliarySymbolInfo` | `Name` | Pointer to a null-terminated char string with the helper name | Global variables used: | Global Name | Type | Purpose | @@ -48,7 +48,7 @@ bool TryGetAuxiliarySymbolName(TargetPointer ip, out string? symbolName) if (address == codePointer && namePointer != TargetPointer.Null) { - symbolName = target.ReadUtf16String(namePointer); + symbolName = target.ReadUtf8String(namePointer); return true; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/JitHelperInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AuxiliarySymbolInfo.cs similarity index 100% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/JitHelperInfo.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/AuxiliarySymbolInfo.cs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs index 1bda95b27c349d..6c044447446cdd 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/OutputBufferHelpers.cs @@ -28,7 +28,7 @@ public static unsafe void CopyUtf8StringToBuffer(byte* stringBuf, uint bufferSiz { int byteCount = Encoding.UTF8.GetByteCount(str); if (neededBufferSize is not null) - *neededBufferSize = (uint)(byteCount + 1); + *neededBufferSize = checked((uint)(byteCount + 1)); if (stringBuf is not null && bufferSize > 0) { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs index f380d311ee6578..7db6dcf45a684a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -2043,7 +2043,7 @@ int ISOSDacInterface.GetJitHelperFunctionName(ClrDataAddress ip, uint count, byt uint needed = 0; OutputBufferHelpers.CopyUtf8StringToBuffer(name, count, &needed, symbolName); - if (needed > count) + if (needed > count && name != null) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; if (pNeeded != null) *pNeeded = needed; From 5493fe6ea05de189ca156a43f11a291fc00b1b40 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Mon, 30 Mar 2026 14:31:56 -0700 Subject: [PATCH 4/4] update dac --- docs/design/datacontracts/AuxiliarySymbols.md | 4 +- src/coreclr/debug/daccess/daccess.cpp | 48 +++---------------- src/coreclr/debug/daccess/dacimpl.h | 3 +- src/coreclr/inc/dacvars.h | 2 +- src/coreclr/vm/jithelpers.cpp | 2 +- src/coreclr/vm/jitinterface.cpp | 2 +- src/coreclr/vm/jitinterface.h | 6 +-- .../Contracts/AuxiliarySymbols_1.cs | 4 +- .../SOSDacImpl.cs | 21 ++++++-- .../cdac/tests/AuxiliarySymbolsTests.cs | 4 +- 10 files changed, 37 insertions(+), 59 deletions(-) diff --git a/docs/design/datacontracts/AuxiliarySymbols.md b/docs/design/datacontracts/AuxiliarySymbols.md index 3849a6674f5f4b..69cc4f7d35a53f 100644 --- a/docs/design/datacontracts/AuxiliarySymbols.md +++ b/docs/design/datacontracts/AuxiliarySymbols.md @@ -36,11 +36,11 @@ bool TryGetAuxiliarySymbolName(TargetPointer ip, out string? symbolName) TargetCodePointer codePointer = CodePointerFromAddress(ip); TargetPointer helperArray = target.ReadGlobalPointer("AuxiliarySymbols"); - int count = target.Read(target.ReadGlobalPointer("AuxiliarySymbolCount")); + uint count = target.Read(target.ReadGlobalPointer("AuxiliarySymbolCount")); uint entrySize = /* AuxiliarySymbolInfo size */; - for (int i = 0; i < count; i++) + for (uint i = 0; i < count; i++) { TargetPointer entryAddr = helperArray + (i * entrySize); TargetCodePointer address = target.ReadCodePointer(entryAddr + /* AuxiliarySymbolInfo::Address offset */); diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index 93062186b89fc9..661648e67afe50 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -5295,50 +5295,14 @@ ClrDataAccess::GetFullMethodName( } PCSTR -ClrDataAccess::GetJitHelperName( - IN TADDR address, - IN bool dynamicHelpersOnly /*=false*/ - ) +ClrDataAccess::GetJitHelperName(IN TADDR address) { - const static PCSTR s_rgHelperNames[] = { -#define JITHELPER(code,fn,sig) #code, -#include - }; - static_assert(ARRAY_SIZE(s_rgHelperNames) == CORINFO_HELP_COUNT); - -#ifdef TARGET_UNIX - if (!dynamicHelpersOnly) -#else - if (!dynamicHelpersOnly && g_runtimeLoadedBaseAddress <= address && - address < g_runtimeLoadedBaseAddress + g_runtimeVirtualSize) -#endif // TARGET_UNIX - { - // Read the whole table from the target in one shot for better performance - VMHELPDEF * pTable = static_cast( - PTR_READ(dac_cast(&hlpFuncTable), CORINFO_HELP_COUNT * sizeof(VMHELPDEF))); - - for (int i = 0; i < CORINFO_HELP_COUNT; i++) - { - if (address == pTable[i].pfnHelper) - return s_rgHelperNames[i]; - } - } - - // Check if its a dynamically generated JIT helper - const static CorInfoHelpFunc s_rgDynamicHCallIds[] = { -#define DYNAMICJITHELPER(code, fn, binderId) code, -#define JITHELPER(code, fn, binderId) -#include - }; - - // Read the whole table from the target in one shot for better performance - VMHELPDEF * pDynamicTable = static_cast( - PTR_READ(dac_cast(&hlpDynamicFuncTable), DYNAMIC_CORINFO_HELP_COUNT * sizeof(VMHELPDEF))); - for (unsigned d = 0; d < DYNAMIC_CORINFO_HELP_COUNT; d++) + PCODE pCode = PINSTRToPCODE(address); + for (unsigned i = 0; i < g_auxiliarySymbolCount; i++) { - if (address == pDynamicTable[d].pfnHelper) + if (pCode == hlpAuxiliarySymbolTable[i].pfnAuxiliarySymbol) { - return s_rgHelperNames[s_rgDynamicHCallIds[d]]; + return hlpAuxiliarySymbolTable[i].name; } } @@ -5565,7 +5529,7 @@ ClrDataAccess::RawGetMethodName( // Do not waste time looking up name for static helper. Debugger can get the actual name from .pdb. PCSTR pHelperName; - pHelperName = GetJitHelperName(TO_TADDR(address), true /* dynamicHelpersOnly */); + pHelperName = GetJitHelperName(TO_TADDR(address)); if (pHelperName != NULL) { if (displacement) diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index e3b08fe128313c..d1c03c7df951f5 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -1205,8 +1205,7 @@ class ClrDataAccess Thread* FindClrThreadByTaskId(ULONG64 taskId); HRESULT IsPossibleCodeAddress(IN TADDR address); - PCSTR GetJitHelperName(IN TADDR address, - IN bool dynamicHelpersOnly = false); + PCSTR GetJitHelperName(IN TADDR address); HRESULT GetFullMethodName(IN MethodDesc* methodDesc, IN ULONG32 symbolChars, IN ULONG32* symbolLen, diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 37b34c621cc76a..5c59425117ee75 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -93,7 +93,7 @@ DEFINE_DACVAR(PTR_InterpreterCodeManager, ExecutionManager__m_pInterpreterCodeMa DEFINE_DACVAR_NO_DUMP(VMHELPDEF *, dac__hlpFuncTable, ::hlpFuncTable) DEFINE_DACVAR(VMHELPDEF *, dac__hlpDynamicFuncTable, ::hlpDynamicFuncTable) DEFINE_DACVAR(VMAUXILIARYSYMBOLDEF *, dac__hlpAuxiliarySymbolTable, ::hlpAuxiliarySymbolTable) -DEFINE_DACVAR(int, dac__g_auxiliarySymbolCount, ::g_auxiliarySymbolCount) +DEFINE_DACVAR(DWORD, dac__g_auxiliarySymbolCount, ::g_auxiliarySymbolCount) DEFINE_DACVAR(PTR_StubManager, StubManager__g_pFirstManager, StubManager::g_pFirstManager) DEFINE_DACVAR(PTR_PrecodeStubManager, PrecodeStubManager__g_pManager, PrecodeStubManager::g_pManager) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 612c418f05bdb0..af477ea52524dc 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -2507,7 +2507,7 @@ void _SetJitHelperFunction(DynamicCorInfoHelpFunc ftnNum, void * pFunc) } VMAUXILIARYSYMBOLDEF hlpAuxiliarySymbolTable[MAX_AUXILIARY_SYMBOLS]; -int g_auxiliarySymbolCount = 0; +DWORD g_auxiliarySymbolCount = 0; void SetAuxiliarySymbol(void* pFunc, const char* name) { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index b5229e2444c101..32a3aa33cec73d 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -82,7 +82,7 @@ GARY_IMPL(VMHELPDEF, hlpFuncTable, CORINFO_HELP_COUNT); GARY_IMPL(VMHELPDEF, hlpDynamicFuncTable, DYNAMIC_CORINFO_HELP_COUNT); GARY_IMPL(VMAUXILIARYSYMBOLDEF, hlpAuxiliarySymbolTable, MAX_AUXILIARY_SYMBOLS); -GVAL_IMPL_INIT(int, g_auxiliarySymbolCount, 0); +GVAL_IMPL_INIT(DWORD, g_auxiliarySymbolCount, 0); #else // DACCESS_COMPILE diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 7db6d694da7599..fed6eb751560ff 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -1008,7 +1008,7 @@ struct VMHELPDEF struct VMAUXILIARYSYMBOLDEF { PCODE pfnAuxiliarySymbol; - const char* name; + PTR_CSTR name; }; #define MAX_AUXILIARY_SYMBOLS 7 @@ -1017,13 +1017,13 @@ struct VMAUXILIARYSYMBOLDEF GARY_DECL(VMHELPDEF, hlpFuncTable, CORINFO_HELP_COUNT); GARY_DECL(VMAUXILIARYSYMBOLDEF, hlpAuxiliarySymbolTable, MAX_AUXILIARY_SYMBOLS); -GVAL_DECL(int, g_auxiliarySymbolCount); +GVAL_DECL(DWORD, g_auxiliarySymbolCount); #else extern "C" const VMHELPDEF hlpFuncTable[CORINFO_HELP_COUNT]; extern "C" VMAUXILIARYSYMBOLDEF hlpAuxiliarySymbolTable[MAX_AUXILIARY_SYMBOLS]; -extern "C" int g_auxiliarySymbolCount; +extern "C" DWORD g_auxiliarySymbolCount; #ifdef FEATURE_PORTABLE_ENTRYPOINTS extern "C" PCODE hlpFuncEntryPoints[CORINFO_HELP_COUNT]; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs index 9af585a4ef5d26..a84f6f3f800cfd 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/AuxiliarySymbols_1.cs @@ -21,12 +21,12 @@ bool IAuxiliarySymbols.TryGetAuxiliarySymbolName(TargetPointer ip, [NotNullWhen( TargetCodePointer codePointer = CodePointerUtils.CodePointerFromAddress(ip, _target); TargetPointer helperArrayPtr = _target.ReadGlobalPointer(Constants.Globals.AuxiliarySymbols); - int helperCount = _target.Read(_target.ReadGlobalPointer(Constants.Globals.AuxiliarySymbolCount)); + uint helperCount = _target.Read(_target.ReadGlobalPointer(Constants.Globals.AuxiliarySymbolCount)); Target.TypeInfo typeInfo = _target.GetTypeInfo(DataType.AuxiliarySymbolInfo); uint entrySize = typeInfo.Size!.Value; - for (int i = 0; i < helperCount; i++) + for (uint i = 0; i < helperCount; i++) { TargetPointer entryAddr = helperArrayPtr + (ulong)(i * entrySize); Data.AuxiliarySymbolInfo entry = _target.ProcessedData.GetOrAdd(entryAddr); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs index 7db6dcf45a684a..adf920f3160154 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -2032,9 +2032,6 @@ int ISOSDacInterface.GetILForModule(ClrDataAddress moduleAddr, int rva, ClrDataA } int ISOSDacInterface.GetJitHelperFunctionName(ClrDataAddress ip, uint count, byte* name, uint* pNeeded) { - // There is deliberately no debug validation here because we change behavior - // to only handle the JIT helpers that cannot be handled as unmanaged or managed symbols - // and to provide more informative names. int hr = HResults.S_OK; try { @@ -2052,6 +2049,24 @@ int ISOSDacInterface.GetJitHelperFunctionName(ClrDataAddress ip, uint count, byt { hr = ex.HResult; } +#if DEBUG + if (_legacyImpl is not null) + { + byte[]? nameLocal = name != null && count > 0 ? new byte[count] : null; + uint neededLocal; + int hrLocal; + fixed (byte* ptr = nameLocal) + { + hrLocal = _legacyImpl.GetJitHelperFunctionName(ip, count, ptr, &neededLocal); + } + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + { + Debug.Assert(pNeeded == null || *pNeeded == neededLocal); + Debug.Assert(name == null || new ReadOnlySpan(name, (int)neededLocal).SequenceEqual(nameLocal!.AsSpan(0, (int)neededLocal))); + } + } +#endif return hr; } diff --git a/src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs b/src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs index 2619483bcf19b1..84c744a50b3843 100644 --- a/src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs +++ b/src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs @@ -65,8 +65,8 @@ private static Target CreateTarget( } // Allocate global for the count - MockMemorySpace.HeapFragment countFragment = allocator.Allocate(sizeof(int), "HelperCount"); - targetTestHelpers.Write(countFragment.Data, helpers.Length); + MockMemorySpace.HeapFragment countFragment = allocator.Allocate(sizeof(uint), "HelperCount"); + targetTestHelpers.Write(countFragment.Data, (uint)helpers.Length); builder.AddHeapFragment(countFragment); (string Name, ulong Value)[] globals =