diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs index 1ebab693d898ed..7df736096c1e14 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs @@ -10,6 +10,7 @@ public static class CorDbgHResults public const int CORDBG_E_READVIRTUAL_FAILURE = unchecked((int)0x80131c49); public const int ERROR_BUFFER_OVERFLOW = unchecked((int)0x8007006F); // HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW) public const int CORDBG_E_CLASS_NOT_LOADED = unchecked((int)0x80131303); + public const int CORDBG_E_FUNCTION_NOT_IL = unchecked((int)0x8013130a); public const int CORDBG_E_TARGET_INCONSISTENT = unchecked((int)0x80131c36); public const int CORDBG_S_NOT_ALL_BITS_SET = unchecked((int)0x00131c13); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs index 6ecbff7791bfad..cc0ee0647f916f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs @@ -22,6 +22,7 @@ public enum TokenType : uint mdtTypeDef = 0x02 << 24, mdtFieldDef = 0x04 << 24, mdtMethodDef = 0x06 << 24, + mdtSignature = 0x11 << 24, } public const uint TokenTypeMask = 0xff000000; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index 6cafa6afe3c976..c50021e1f85b22 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -6,6 +6,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using System.Numerics; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -1400,7 +1403,73 @@ public int ResolveExactGenericArgsToken(uint dwExactGenericArgsTokenIndex, ulong } public int GetILCodeAndSig(ulong vmAssembly, uint functionToken, DacDbiTargetBuffer* pTargetBuffer, uint* pLocalSigToken) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetILCodeAndSig(vmAssembly, functionToken, pTargetBuffer, pLocalSigToken) : HResults.E_NOTIMPL; + { + *pTargetBuffer = default; + *pLocalSigToken = (uint)EcmaMetadataUtils.TokenType.mdtSignature; + int hr = HResults.S_OK; + try + { + ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromAssemblyPtr(new TargetPointer(vmAssembly)); + + MetadataReader mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle) + ?? throw new InvalidOperationException("Module has no metadata."); + MethodDefinitionHandle mdMethodHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(functionToken)); + MethodDefinition methodDef = mdReader.GetMethodDefinition(mdMethodHandle); + + // Reject anything whose metadata CodeType isn't IL. + if ((methodDef.ImplAttributes & MethodImplAttributes.CodeTypeMask) != MethodImplAttributes.IL) + throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_FUNCTION_NOT_IL)!; + + ModuleLookupTables lookupTables = loader.GetLookupTables(moduleHandle); + TargetPointer methodDescPtr = loader.GetModuleLookupMapElement(lookupTables.MethodDefToDesc, functionToken, out _); + if (methodDescPtr != TargetPointer.Null) + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle mdHandle = rts.GetMethodDescHandle(methodDescPtr); + if (!rts.IsIL(mdHandle)) + throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_FUNCTION_NOT_IL)!; + } + + else if (methodDef.RelativeVirtualAddress == 0) + throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_FUNCTION_NOT_IL)!; + + TargetPointer headerPtr = loader.GetILHeader(moduleHandle, functionToken); + if (headerPtr != TargetPointer.Null) + { + int headerSize = HeaderReaderHelpers.GetHeaderSize(_target, headerPtr); + int codeSize = HeaderReaderHelpers.GetCodeSize(_target, headerPtr); + + if (HeaderReaderHelpers.TryGetLocalVarSigToken(_target, headerPtr, out int localToken) && localToken != 0) + { + *pLocalSigToken = (uint)localToken; + } + + pTargetBuffer->pAddress = headerPtr.Value + (ulong)headerSize; + pTargetBuffer->cbSize = (uint)codeSize; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + DacDbiTargetBuffer bufferLocal = default; + uint sigLocal; + int hrLocal = _legacy.GetILCodeAndSig(vmAssembly, functionToken, &bufferLocal, &sigLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + { + Debug.Assert(pTargetBuffer->pAddress == bufferLocal.pAddress, $"cDAC ILAddr: 0x{pTargetBuffer->pAddress:X}, DAC ILAddr: 0x{bufferLocal.pAddress:X}"); + Debug.Assert(pTargetBuffer->cbSize == bufferLocal.cbSize, $"cDAC ILSize: {pTargetBuffer->cbSize}, DAC ILSize: {bufferLocal.cbSize}"); + Debug.Assert(*pLocalSigToken == sigLocal, $"cDAC LocalSig: 0x{*pLocalSigToken:X}, DAC LocalSig: 0x{sigLocal:X}"); + } + } +#endif + return hr; + } public int GetNativeCodeInfo(ulong vmAssembly, uint functionToken, nint pJitManagerList) => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetNativeCodeInfo(vmAssembly, functionToken, pJitManagerList) : HResults.E_NOTIMPL; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/HeaderReaderHelpers.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/HeaderReaderHelpers.cs index fa840788a05b56..b14ddd7b5f1012 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/HeaderReaderHelpers.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/HeaderReaderHelpers.cs @@ -1,6 +1,7 @@ // 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.Legacy; @@ -14,6 +15,34 @@ internal enum CorILMethodFlags } internal static class HeaderReaderHelpers { + public static int GetHeaderSize(Target target, TargetPointer ilHeader) + { + // see ECMA-335 II.25.4 + ushort sizeAndFlags = target.Read(ilHeader); + CorILMethodFlags flags = (CorILMethodFlags)(sizeAndFlags & (int)CorILMethodFlags.CorILMethod_FormatMask); + + return flags switch + { + CorILMethodFlags.CorILMethod_TinyFormat => 1, + CorILMethodFlags.CorILMethod_FatFormat => 12, + _ => throw new BadImageFormatException("Invalid IL method header."), + }; + } + + public static int GetCodeSize(Target target, TargetPointer ilHeader) + { + // see ECMA-335 II.25.4 + ushort sizeAndFlags = target.Read(ilHeader); + CorILMethodFlags flags = (CorILMethodFlags)(sizeAndFlags & (int)CorILMethodFlags.CorILMethod_FormatMask); + + return flags switch + { + CorILMethodFlags.CorILMethod_TinyFormat => sizeAndFlags >> 2, + CorILMethodFlags.CorILMethod_FatFormat => (int)target.Read(ilHeader + 4), + _ => throw new BadImageFormatException("Invalid IL method header."), + }; + } + public static bool TryGetLocalVarSigToken(Target target, TargetPointer ilHeader, out int localVarSigToken) { // see ECMA-335 II.25.4