Skip to content

Commit 2790c60

Browse files
Copilotrcj1
andauthored
Implement GetIlCodeAndSig DacDbi API in cDAC (#128354)
Port the `GetILCodeAndSig` DacDbi entry point from the native DAC to the managed cDAC, returning the target-address/size of a method's IL and its local-variable signature token. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> Co-authored-by: rcj1 <rachel.jarvi@gmail.com>
1 parent c7bd259 commit 2790c60

4 files changed

Lines changed: 103 additions & 6 deletions

File tree

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public static class CorDbgHResults
1010
public const int CORDBG_E_READVIRTUAL_FAILURE = unchecked((int)0x80131c49);
1111
public const int ERROR_BUFFER_OVERFLOW = unchecked((int)0x8007006F); // HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW)
1212
public const int CORDBG_E_CLASS_NOT_LOADED = unchecked((int)0x80131303);
13+
public const int CORDBG_E_FUNCTION_NOT_IL = unchecked((int)0x8013130a);
1314
public const int CORDBG_E_TARGET_INCONSISTENT = unchecked((int)0x80131c36);
1415
public const int CORDBG_S_NOT_ALL_BITS_SET = unchecked((int)0x00131c13);
1516
public const int CORDBG_E_NON_MATCHING_CONTEXT = unchecked((int)0x80131327);

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public enum TokenType : uint
2222
mdtTypeDef = 0x02 << 24,
2323
mdtFieldDef = 0x04 << 24,
2424
mdtMethodDef = 0x06 << 24,
25+
mdtSignature = 0x11 << 24,
2526
}
2627

2728
public const uint TokenTypeMask = 0xff000000;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Collections.Immutable;
88
using System.Diagnostics;
99
using System.Linq;
10+
using System.Reflection;
1011
using System.Reflection.Metadata;
1112
using System.Reflection.Metadata.Ecma335;
1213
using System.Numerics;
@@ -1693,7 +1694,72 @@ public int ResolveExactGenericArgsToken(uint dwExactGenericArgsTokenIndex, ulong
16931694
}
16941695

16951696
public int GetILCodeAndSig(ulong vmAssembly, uint functionToken, DacDbiTargetBuffer* pTargetBuffer, uint* pLocalSigToken)
1696-
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetILCodeAndSig(vmAssembly, functionToken, pTargetBuffer, pLocalSigToken) : HResults.E_NOTIMPL;
1697+
{
1698+
int hr = HResults.S_OK;
1699+
try
1700+
{
1701+
*pTargetBuffer = default;
1702+
*pLocalSigToken = (uint)EcmaMetadataUtils.TokenType.mdtSignature;
1703+
ILoader loader = _target.Contracts.Loader;
1704+
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromAssemblyPtr(new TargetPointer(vmAssembly));
1705+
1706+
MetadataReader mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle)
1707+
?? throw new InvalidOperationException("Module has no metadata.");
1708+
MethodDefinitionHandle mdMethodHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(functionToken));
1709+
MethodDefinition methodDef = mdReader.GetMethodDefinition(mdMethodHandle);
1710+
1711+
// Reject anything whose metadata CodeType isn't IL.
1712+
if ((methodDef.ImplAttributes & MethodImplAttributes.CodeTypeMask) != MethodImplAttributes.IL)
1713+
throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_FUNCTION_NOT_IL)!;
1714+
1715+
ModuleLookupTables lookupTables = loader.GetLookupTables(moduleHandle);
1716+
TargetPointer methodDescPtr = loader.GetModuleLookupMapElement(lookupTables.MethodDefToDesc, functionToken, out _);
1717+
if (methodDescPtr != TargetPointer.Null)
1718+
{
1719+
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
1720+
MethodDescHandle mdHandle = rts.GetMethodDescHandle(methodDescPtr);
1721+
if (!rts.IsIL(mdHandle))
1722+
throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_FUNCTION_NOT_IL)!;
1723+
}
1724+
else if (methodDef.RelativeVirtualAddress == 0)
1725+
throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_FUNCTION_NOT_IL)!;
1726+
1727+
TargetPointer headerPtr = loader.GetILHeader(moduleHandle, functionToken);
1728+
if (headerPtr != TargetPointer.Null)
1729+
{
1730+
int headerSize = HeaderReaderHelpers.GetHeaderSize(_target, headerPtr);
1731+
int codeSize = HeaderReaderHelpers.GetCodeSize(_target, headerPtr);
1732+
1733+
if (HeaderReaderHelpers.TryGetLocalVarSigToken(_target, headerPtr, out int localToken) && localToken != 0)
1734+
{
1735+
*pLocalSigToken = (uint)localToken;
1736+
}
1737+
1738+
pTargetBuffer->pAddress = headerPtr.Value + (ulong)headerSize;
1739+
pTargetBuffer->cbSize = (uint)codeSize;
1740+
}
1741+
}
1742+
catch (System.Exception ex)
1743+
{
1744+
hr = ex.HResult;
1745+
}
1746+
#if DEBUG
1747+
if (_legacy is not null)
1748+
{
1749+
DacDbiTargetBuffer bufferLocal = default;
1750+
uint sigLocal;
1751+
int hrLocal = _legacy.GetILCodeAndSig(vmAssembly, functionToken, &bufferLocal, &sigLocal);
1752+
Debug.ValidateHResult(hr, hrLocal);
1753+
if (hr == HResults.S_OK)
1754+
{
1755+
Debug.Assert(pTargetBuffer->pAddress == bufferLocal.pAddress, $"cDAC ILAddr: 0x{pTargetBuffer->pAddress:X}, DAC ILAddr: 0x{bufferLocal.pAddress:X}");
1756+
Debug.Assert(pTargetBuffer->cbSize == bufferLocal.cbSize, $"cDAC ILSize: {pTargetBuffer->cbSize}, DAC ILSize: {bufferLocal.cbSize}");
1757+
Debug.Assert(*pLocalSigToken == sigLocal, $"cDAC LocalSig: 0x{*pLocalSigToken:X}, DAC LocalSig: 0x{sigLocal:X}");
1758+
}
1759+
}
1760+
#endif
1761+
return hr;
1762+
}
16971763

16981764
public int GetNativeCodeInfo(ulong vmAssembly, uint functionToken, nint pJitManagerList)
16991765
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetNativeCodeInfo(vmAssembly, functionToken, pJitManagerList) : HResults.E_NOTIMPL;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/HeaderReaderHelpers.cs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,55 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using System.Runtime.InteropServices;
56

67
namespace Microsoft.Diagnostics.DataContractReader.Legacy;
78

89
internal enum CorILMethodFlags
910
{
10-
CorILMethod_FormatShift = 3,
11-
CorILMethod_FormatMask = (1 << CorILMethod_FormatShift) - 1,
11+
CorILMethod_TinyFormatMask = 0x3,
12+
CorILMethod_FatFormatMask = 0x7,
1213
CorILMethod_TinyFormat = 0x0002,
1314
CorILMethod_FatFormat = 0x0003,
1415
}
1516
internal static class HeaderReaderHelpers
1617
{
18+
// See ECMA-335 II.25.4.1
19+
private static bool IsTiny(byte firstByte)
20+
=> (firstByte & (int)CorILMethodFlags.CorILMethod_TinyFormatMask) == (int)CorILMethodFlags.CorILMethod_TinyFormat;
21+
22+
private static bool IsFat(byte firstByte)
23+
=> (firstByte & (int)CorILMethodFlags.CorILMethod_FatFormatMask) == (int)CorILMethodFlags.CorILMethod_FatFormat;
24+
25+
public static int GetHeaderSize(Target target, TargetPointer ilHeader)
26+
{
27+
// see ECMA-335 II.25.4
28+
byte firstByte = target.Read<byte>(ilHeader);
29+
if (IsTiny(firstByte))
30+
return 1;
31+
if (IsFat(firstByte))
32+
return 12;
33+
throw new BadImageFormatException("Invalid IL method header.");
34+
}
35+
36+
public static int GetCodeSize(Target target, TargetPointer ilHeader)
37+
{
38+
// see ECMA-335 II.25.4
39+
byte firstByte = target.Read<byte>(ilHeader);
40+
if (IsTiny(firstByte))
41+
return firstByte >> 2;
42+
if (IsFat(firstByte))
43+
return (int)target.Read<uint>(ilHeader + 4);
44+
throw new BadImageFormatException("Invalid IL method header.");
45+
}
46+
1747
public static bool TryGetLocalVarSigToken(Target target, TargetPointer ilHeader, out int localVarSigToken)
1848
{
1949
// see ECMA-335 II.25.4
2050
localVarSigToken = 0;
21-
ushort sizeAndFlags = target.Read<ushort>(ilHeader); // get flags and size of il header
22-
CorILMethodFlags flags = (CorILMethodFlags)(sizeAndFlags & (int)CorILMethodFlags.CorILMethod_FormatMask);
23-
if (flags != CorILMethodFlags.CorILMethod_FatFormat)
51+
byte firstByte = target.Read<byte>(ilHeader);
52+
if (!IsFat(firstByte))
2453
return false;
2554

2655
localVarSigToken = target.Read<int>(ilHeader + 8);

0 commit comments

Comments
 (0)