Skip to content

Commit b9fbc0d

Browse files
authored
[cDAC] Implement GetMethodTableSlot and GetMethodTableSlotEnum (#118960)
* Implement GetMethodTableSlot and GetMethodTableSlotEnum. * Introduce new APIs in IRuntimeTypeSystem for obtaining vtable slots, entry points, and introduced methods. * Refactor code to unify slot and method descriptor retrieval logic. * Add conversion helpers for TargetCodePointer to ClrDataAddress.
1 parent ff381b7 commit b9fbc0d

File tree

6 files changed

+496
-25
lines changed

6 files changed

+496
-25
lines changed

docs/design/datacontracts/RuntimeTypeSystem.md

Lines changed: 92 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ partial interface IRuntimeTypeSystem : IContract
4242
public virtual TargetPointer GetParentMethodTable(TypeHandle typeHandle);
4343

4444
public virtual TargetPointer GetMethodDescForSlot(TypeHandle typeHandle, ushort slot);
45+
public virtual IEnumerable<TargetPointer> GetIntroducedMethodDescs(TypeHandle methodTable);
46+
public virtual TargetCodePointer GetSlot(TypeHandle typeHandle, uint slot);
4547

4648
public virtual uint GetBaseSize(TypeHandle typeHandle);
4749
// The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes)
@@ -57,6 +59,7 @@ partial interface IRuntimeTypeSystem : IContract
5759

5860
// Returns an ECMA-335 TypeDef table token for this type, or for its generic type definition if it is a generic instantiation
5961
public virtual uint GetTypeDefToken(TypeHandle typeHandle);
62+
public virtual ushort GetNumVtableSlots(TypeHandle typeHandle);
6063
public virtual ushort GetNumMethods(TypeHandle typeHandle);
6164
// Returns the ECMA 335 TypeDef table Flags value (a bitmask of TypeAttributes) for this type,
6265
// or for its generic type definition if it is a generic instantiation
@@ -169,8 +172,12 @@ partial interface IRuntimeTypeSystem : IContract
169172
public virtual TargetPointer GetAddressOfNativeCodeSlot(MethodDescHandle methodDesc);
170173

171174
// Get an instruction pointer that can be called to cause the MethodDesc to be executed
175+
// Requires the entry point to be stable
172176
public virtual TargetCodePointer GetNativeCode(MethodDescHandle methodDesc);
173177

178+
// Get an instruction pointer that can be called to cause the MethodDesc to be executed
179+
public virtual TargetCodePointer GetMethodEntryPointIfExists(MethodDescHandle methodDesc);
180+
174181
// Gets the GCStressCodeCopy pointer if available, otherwise returns TargetPointer.Null
175182
public virtual TargetPointer GetGCStressCodeCopy(MethodDescHandle methodDesc);
176183
}
@@ -470,6 +477,15 @@ Contracts used:
470477
return (uint)(typeHandle.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24));
471478
}
472479

480+
public ushort GetNumVtableSlots(TypeHandle typeHandle)
481+
{
482+
if (!typeHandle.IsMethodTable())
483+
return 0;
484+
MethodTable methodTable = _methodTables[typeHandle.Address];
485+
ushort numNonVirtualSlots = methodTable.IsCanonMT ? GetClassData(typeHandle).NumNonVirtualSlots : (ushort)0;
486+
return checked((ushort)(methodTable.NumVirtuals + numNonVirtualSlots));
487+
}
488+
473489
public ushort GetNumMethods(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : GetClassData(TypeHandle).NumMethods;
474490

475491
public uint GetTypeDefTypeAttributes(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : GetClassData(TypeHandle).CorTypeAttr;
@@ -1302,6 +1318,30 @@ Getting the native code pointer for methods with a NativeCodeSlot or a stable en
13021318

13031319
return GetStableEntryPoint(methodDescHandle.Address, md);
13041320
}
1321+
1322+
public TargetCodePointer IRuntimeTypeSystem.GetMethodEntryPointIfExists(MethodDescHandle methodDescHandle)
1323+
{
1324+
MethodDesc md = _methodDescs[methodDescHandle.Address];
1325+
return GetMethodEntryPointIfExists(methodDescHandle.Address, md);
1326+
}
1327+
```
1328+
1329+
Getting the value of a slot of a MethodTable
1330+
```csharp
1331+
public TargetCodePointer GetSlot(TypeHandle typeHandle, uint slot)
1332+
{
1333+
// based on MethodTable::GetSlot(uint slotNumber)
1334+
if (!typeHandle.IsMethodTable())
1335+
throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable");
1336+
1337+
if (slot < GetNumVtableSlots(typeHandle))
1338+
{
1339+
TargetPointer slotPtr = GetAddressOfSlot(typeHandle, slot);
1340+
return _target.ReadCodePointer(slotPtr);
1341+
}
1342+
1343+
return TargetCodePointer.Null;
1344+
}
13051345
```
13061346

13071347
Getting a MethodDesc for a certain slot in a MethodTable
@@ -1353,33 +1393,78 @@ Getting a MethodDesc for a certain slot in a MethodTable
13531393
}
13541394
}
13551395

1356-
public TargetPointer GetMethodDescForSlot(TypeHandle methodTable, ushort slot)
1396+
public IEnumerable<TargetPointer> GetIntroducedMethodDescs(TypeHandle typeHandle)
1397+
{
1398+
if (!typeHandle.IsMethodTable())
1399+
throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable");
1400+
1401+
TypeHandle canonMT = GetTypeHandle(GetCanonicalMethodTable(typeHandle));
1402+
foreach (MethodDescHandle mdh in GetIntroducedMethods(canonMT))
1403+
{
1404+
yield return mdh.Address;
1405+
}
1406+
}
1407+
1408+
// Uses GetMethodDescForVtableSlot if slot is less than the number of vtable slots
1409+
// otherwise looks for the slot in the introduced methods
1410+
public TargetPointer GetMethodDescForSlot(TypeHandle typeHandle, ushort slot)
13571411
{
13581412
if (!typeHandle.IsMethodTable())
13591413
throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable");
13601414

1415+
TypeHandle canonMT = GetTypeHandle(GetCanonicalMethodTable(typeHandle));
1416+
if (slot < GetNumVtableSlots(canonMT))
1417+
{
1418+
return GetMethodDescForVtableSlot(canonMT, slot);
1419+
}
1420+
else
1421+
{
1422+
foreach (MethodDescHandle mdh in GetIntroducedMethods(canonMT))
1423+
{
1424+
MethodDesc md = _methodDescs[mdh.Address];
1425+
if (md.Slot == slot)
1426+
{
1427+
return mdh.Address;
1428+
}
1429+
}
1430+
return TargetPointer.Null;
1431+
}
1432+
}
1433+
1434+
private TargetPointer GetMethodDescForVtableSlot(TypeHandle methodTable, ushort slot)
1435+
{
1436+
// based on MethodTable::GetMethodDescForSlot_NoThrow
1437+
if (!typeHandle.IsMethodTable())
1438+
throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable");
1439+
13611440
TargetPointer cannonMTPTr = GetCanonicalMethodTable(typeHandle);
13621441
TypeHandle canonMT = GetTypeHandle(cannonMTPTr);
1442+
if (slot >= GetNumVtableSlots(canonMT))
1443+
throw new ArgumentException(nameof(slot), "Slot number is greater than the number of slots");
1444+
13631445
TargetPointer slotPtr = GetAddressOfSlot(canonMT, slot);
13641446
TargetCodePointer pCode = _target.ReadCodePointer(slotPtr);
13651447

13661448
if (pCode == TargetCodePointer.Null)
13671449
{
1368-
// if pCode is null, we iterate through the method descs in the MT
1369-
while (true) // arbitrary limit to avoid infinite loop
1450+
TargetPointer lookupMTPtr = cannonMTPTr;
1451+
while (lookupMTPtr != TargetPointer.Null)
13701452
{
1371-
foreach (MethodDescHandle mdh in GetIntroducedMethods(canonMT))
1453+
// if pCode is null, we iterate through the method descs in the MT.
1454+
TypeHandle lookupMT = GetTypeHandle(lookupMTPtr);
1455+
foreach (MethodDescHandle mdh in GetIntroducedMethods(lookupMT))
13721456
{
13731457
MethodDesc md = _methodDescs[mdh.Address];
1374-
1375-
// if a MethodDesc matches the slot, return that MethodDesc
13761458
if (md.Slot == slot)
13771459
{
13781460
return mdh.Address;
13791461
}
13801462
}
1381-
canonMT = GetTypeHandle(GetCanonicalMethodTable(GetTypeHandle(GetParentMethodTable(canonMT))));
1463+
lookupMTPtr = GetParentMethodTable(lookupMT);
1464+
if (lookupMTPtr != TargetPointer.Null)
1465+
lookupMTPtr = GetCanonicalMethodTable(GetTypeHandle(lookupMTPtr));
13821466
}
1467+
return TargetPointer.Null;
13831468
}
13841469

13851470
return GetMethodDescForEntrypoint(pCode);

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Collections.Generic;
56

67
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
78

@@ -89,6 +90,8 @@ public interface IRuntimeTypeSystem : IContract
8990
TargetPointer GetParentMethodTable(TypeHandle typeHandle) => throw new NotImplementedException();
9091

9192
TargetPointer GetMethodDescForSlot(TypeHandle methodTable, ushort slot) => throw new NotImplementedException();
93+
IEnumerable<TargetPointer> GetIntroducedMethodDescs(TypeHandle methodTable) => throw new NotImplementedException();
94+
TargetCodePointer GetSlot(TypeHandle typeHandle, uint slot) => throw new NotImplementedException();
9295

9396
uint GetBaseSize(TypeHandle typeHandle) => throw new NotImplementedException();
9497
// The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes)
@@ -104,6 +107,7 @@ public interface IRuntimeTypeSystem : IContract
104107

105108
// Returns an ECMA-335 TypeDef table token for this type, or for its generic type definition if it is a generic instantiation
106109
uint GetTypeDefToken(TypeHandle typeHandle) => throw new NotImplementedException();
110+
ushort GetNumVtableSlots(TypeHandle typeHandle) => throw new NotImplementedException();
107111
ushort GetNumMethods(TypeHandle typeHandle) => throw new NotImplementedException();
108112
// Returns the ECMA 335 TypeDef table Flags value (a bitmask of TypeAttributes) for this type,
109113
// or for its generic type definition if it is a generic instantiation
@@ -175,6 +179,7 @@ public interface IRuntimeTypeSystem : IContract
175179
TargetPointer GetMethodDescVersioningState(MethodDescHandle methodDesc) => throw new NotImplementedException();
176180

177181
TargetCodePointer GetNativeCode(MethodDescHandle methodDesc) => throw new NotImplementedException();
182+
TargetCodePointer GetMethodEntryPointIfExists(MethodDescHandle methodDesc) => throw new NotImplementedException();
178183

179184
ushort GetSlotNumber(MethodDescHandle methodDesc) => throw new NotImplementedException();
180185

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

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,14 @@ public uint GetTypeDefToken(TypeHandle typeHandle)
410410
MethodTable methodTable = _methodTables[typeHandle.Address];
411411
return (uint)(methodTable.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24));
412412
}
413+
public ushort GetNumVtableSlots(TypeHandle typeHandle)
414+
{
415+
if (!typeHandle.IsMethodTable())
416+
return 0;
417+
MethodTable methodTable = _methodTables[typeHandle.Address];
418+
ushort numNonVirtualSlots = methodTable.IsCanonMT ? GetClassData(typeHandle).NumNonVirtualSlots : (ushort)0;
419+
return checked((ushort)(methodTable.NumVirtuals + numNonVirtualSlots));
420+
}
413421
public ushort GetNumMethods(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumMethods;
414422
public uint GetTypeDefTypeAttributes(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : GetClassData(typeHandle).CorTypeAttr;
415423
public ushort GetNumInstanceFields(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumInstanceFields;
@@ -699,15 +707,6 @@ private FunctionPointerRetAndArgs(Target target, TargetPointer typePointer)
699707
}
700708
}
701709

702-
private ushort GetNumVtableSlots(TypeHandle typeHandle)
703-
{
704-
if (!typeHandle.IsMethodTable())
705-
return 0;
706-
MethodTable methodTable = _methodTables[typeHandle.Address];
707-
ushort numNonVirtualSlots = methodTable.IsCanonMT ? GetClassData(typeHandle).NumNonVirtualSlots : (ushort)0;
708-
return checked((ushort)(methodTable.NumVirtuals + numNonVirtualSlots));
709-
}
710-
711710
public MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer)
712711
=> GetMethodDescHandle(methodDescPointer, validate: true);
713712

@@ -1086,33 +1085,80 @@ private IEnumerable<MethodDescHandle> GetIntroducedMethods(TypeHandle typeHandle
10861085
}
10871086
}
10881087

1088+
IEnumerable<TargetPointer> IRuntimeTypeSystem.GetIntroducedMethodDescs(TypeHandle typeHandle)
1089+
{
1090+
if (!typeHandle.IsMethodTable())
1091+
yield break;
1092+
1093+
TypeHandle canonMT = GetTypeHandle(GetCanonicalMethodTable(typeHandle));
1094+
foreach (MethodDescHandle mdh in GetIntroducedMethods(canonMT))
1095+
{
1096+
yield return mdh.Address;
1097+
}
1098+
}
1099+
1100+
// Uses GetMethodDescForVtableSlot if slot is less than the number of vtable slots
1101+
// otherwise looks for the slot in the introduced methods
10891102
TargetPointer IRuntimeTypeSystem.GetMethodDescForSlot(TypeHandle typeHandle, ushort slot)
1103+
{
1104+
if (!typeHandle.IsMethodTable())
1105+
// TypeDesc do not contain any slots.
1106+
return TargetPointer.Null;
1107+
1108+
TypeHandle canonMT = GetTypeHandle(GetCanonicalMethodTable(typeHandle));
1109+
if (slot < GetNumVtableSlots(canonMT))
1110+
{
1111+
return GetMethodDescForVtableSlot(canonMT, slot);
1112+
}
1113+
else
1114+
{
1115+
foreach (MethodDescHandle mdh in GetIntroducedMethods(canonMT))
1116+
{
1117+
MethodDesc md = _methodDescs[mdh.Address];
1118+
if (md.Slot == slot)
1119+
{
1120+
return mdh.Address;
1121+
}
1122+
}
1123+
return TargetPointer.Null;
1124+
}
1125+
}
1126+
1127+
private TargetPointer GetMethodDescForVtableSlot(TypeHandle typeHandle, ushort slot)
10901128
{
10911129
// based on MethodTable::GetMethodDescForSlot_NoThrow
10921130
if (!typeHandle.IsMethodTable())
1093-
throw new ArgumentException($"{nameof(typeHandle)} is not a MethodTable");
1131+
// TypeDesc do not contain any slots.
1132+
throw new ArgumentException(nameof(slot), "Slot number is greater than the number of slots");
10941133

10951134
TargetPointer cannonMTPTr = GetCanonicalMethodTable(typeHandle);
10961135
TypeHandle canonMT = GetTypeHandle(cannonMTPTr);
1136+
if (slot >= GetNumVtableSlots(canonMT))
1137+
throw new ArgumentException(nameof(slot), "Slot number is greater than the number of slots");
1138+
10971139
TargetPointer slotPtr = GetAddressOfSlot(canonMT, slot);
10981140
TargetCodePointer pCode = _target.ReadCodePointer(slotPtr);
10991141

11001142
if (pCode == TargetCodePointer.Null)
11011143
{
1102-
while (canonMT.Address != TargetPointer.Null)
1144+
TargetPointer lookupMTPtr = cannonMTPTr;
1145+
while (lookupMTPtr != TargetPointer.Null)
11031146
{
11041147
// if pCode is null, we iterate through the method descs in the MT.
1105-
foreach (MethodDescHandle mdh in GetIntroducedMethods(canonMT))
1148+
TypeHandle lookupMT = GetTypeHandle(lookupMTPtr);
1149+
foreach (MethodDescHandle mdh in GetIntroducedMethods(lookupMT))
11061150
{
11071151
MethodDesc md = _methodDescs[mdh.Address];
11081152
if (md.Slot == slot)
11091153
{
11101154
return mdh.Address;
11111155
}
11121156
}
1113-
canonMT = GetTypeHandle(GetCanonicalMethodTable(GetTypeHandle(GetParentMethodTable(canonMT))));
1157+
lookupMTPtr = GetParentMethodTable(lookupMT);
1158+
if (lookupMTPtr != TargetPointer.Null)
1159+
lookupMTPtr = GetCanonicalMethodTable(GetTypeHandle(lookupMTPtr));
11141160
}
1115-
Debug.Fail("We should never reach here, as there should always be a MethodDesc for a slot");
1161+
return TargetPointer.Null;
11161162
}
11171163

11181164
return GetMethodDescForEntrypoint(pCode);
@@ -1135,6 +1181,19 @@ private readonly TargetPointer GetMethodDescForEntrypoint(TargetCodePointer pCod
11351181
}
11361182
}
11371183

1184+
TargetCodePointer IRuntimeTypeSystem.GetSlot(TypeHandle typeHandle, uint slot)
1185+
{
1186+
// based on MethodTable::GetSlot(uint slotNumber)
1187+
1188+
if (slot < GetNumVtableSlots(typeHandle))
1189+
{
1190+
TargetPointer slotPtr = GetAddressOfSlot(typeHandle, slot);
1191+
return _target.ReadCodePointer(slotPtr);
1192+
}
1193+
1194+
return TargetCodePointer.Null;
1195+
}
1196+
11381197
TargetPointer IRuntimeTypeSystem.GetAddressOfNativeCodeSlot(MethodDescHandle methodDesc)
11391198
{
11401199
MethodDesc md = _methodDescs[methodDesc.Address];
@@ -1161,6 +1220,12 @@ TargetCodePointer IRuntimeTypeSystem.GetNativeCode(MethodDescHandle methodDescHa
11611220
return GetStableEntryPoint(md);
11621221
}
11631222

1223+
TargetCodePointer IRuntimeTypeSystem.GetMethodEntryPointIfExists(MethodDescHandle methodDescHandle)
1224+
{
1225+
MethodDesc md = _methodDescs[methodDescHandle.Address];
1226+
return GetMethodEntryPointIfExists(md);
1227+
}
1228+
11641229
private TargetCodePointer GetStableEntryPoint(MethodDesc md)
11651230
{
11661231
// TODO(cdac): _ASSERTE(!IsVersionableWithVtableSlotBackpatch());

src/native/managed/cdac/mscordaccore_universal/Legacy/ConversionExtensions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,21 @@ public static ClrDataAddress ToClrDataAddress(this TargetPointer address, Target
2626
}
2727
}
2828

29+
/// <summary>
30+
/// Converts a TargetCodePointer to a ClrDataAddress using sign extension if required.
31+
/// </summary>
32+
public static ClrDataAddress ToClrDataAddress(this TargetCodePointer address, Target target)
33+
{
34+
if (target.PointerSize == sizeof(ulong))
35+
{
36+
return address.Value;
37+
}
38+
else
39+
{
40+
return (ulong)(int)address.Value;
41+
}
42+
}
43+
2944
/// <summary>
3045
/// Converts a ClrDataAddress to a TargetPointer, ensuring the address is within the valid range for the target platform.
3146
/// When overrideCheck is true, this will not check the range and will allow any address. This is used on legacy endpoints which

0 commit comments

Comments
 (0)