Skip to content

Commit 272a83e

Browse files
Implementation of SOSDacApi GetMethodDescName for cDAC (#106169)
Add a number of new `MethodDesc` contract definitions | Contract algorithm on RuntimeTypeSystem | | --- | | `IsGenericMethodDefinition`| |`GetGenericMethodInstantiation`| |`GetMethodToken`| |`IsArrayMethod`| |`IsDynamicMethod`| |`IsStoredSigMethodDesc`| |`IsNoMetadataMethod`| |`IsILStub`| Update cDAC compat asserts in cDAC to always be enabled by using a tls variable in `mscordaccore` Implement `GetMethodDescName` on `ISOSDacInterface` in the `cdacreader` Stub out an implementation of `GetPath` in the `Loader` contract used in a fallback after a fallback. This will need further work, but is included to make sure the code path isn't lost. Fix the `EcmaMetadataReader` to be able to find blobs in the metadata Add ability to read target data from a buffer held on the cdac side using the `Target` class. This was needed to handle signature containing a `CorElementType.Internal`. And finally actually implement the name generation algorithm via a line for line port from the CoreCLR codebase. Contributes to #99302
1 parent 34b41f8 commit 272a83e

23 files changed

+1521
-27
lines changed

docs/design/datacontracts/RuntimeTypeSystem.md

Lines changed: 247 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,51 @@ struct MethodDescHandle
9292
}
9393
```
9494

95+
```csharp
96+
public enum ArrayFunctionType
97+
{
98+
Get = 0,
99+
Set = 1,
100+
Address = 2,
101+
Constructor = 3
102+
}
103+
```
104+
95105
```csharp
96106
partial interface IRuntimeTypeSystem : IContract
97107
{
98108
public virtual MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer);
99109

100110
public virtual TargetPointer GetMethodTable(MethodDescHandle methodDesc);
111+
112+
// Return true for an uninstantiated generic method
113+
public virtual bool IsGenericMethodDefinition(MethodDescHandle methodDesc);
114+
115+
public virtual ReadOnlySpan<TypeHandle> GetGenericMethodInstantiation(MethodDescHandle methodDesc);
116+
117+
// Return mdTokenNil (0x06000000) if the method doesn't have a token, otherwise return the token of the method
118+
public virtual uint GetMethodToken(MethodDescHandle methodDesc);
119+
120+
// Return true if a MethodDesc represents an array method
121+
// An array method is also a StoredSigMethodDesc
122+
public virtual bool IsArrayMethod(MethodDescHandle methodDesc, out ArrayFunctionType functionType);
123+
124+
// Return true if a MethodDesc represents a method without metadata method, either an IL Stub dynamically
125+
// generated by the runtime, or a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class
126+
// Or something else similar.
127+
// A no metadata method is also a StoredSigMethodDesc
128+
public virtual bool IsNoMetadataMethod(MethodDescHandle methodDesc, out ReadOnlySpan<byte> methodName);
129+
130+
// A StoredSigMethodDesc is a MethodDesc for which the signature isn't found in metadata.
131+
public virtual bool IsStoredSigMethodDesc(MethodDescHandle methodDesc, out ReadOnlySpan<byte> signature);
132+
133+
// Return true for a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class
134+
// A DynamicMethod is also a StoredSigMethodDesc, and a NoMetadataMethod
135+
public virtual bool IsDynamicMethod(MethodDescHandle methodDesc);
136+
137+
// Return true if a MethodDesc represents an IL Stub dynamically generated by the runtime
138+
// A IL Stub method is also a StoredSigMethodDesc, and a NoMetadataMethod
139+
public virtual bool IsILStub(MethodDescHandle methodDesc);
101140
}
102141
```
103142

@@ -563,7 +602,8 @@ The version 1 `MethodDesc` APIs depend on the `MethodDescAlignment` global and t
563602

564603
| Global name | Meaning |
565604
| --- | --- |
566-
| `MethodDescAlignment` | `MethodDescChunk` trailing data is allocated in multiples of this constant. The size (in bytes) of each `MethodDesc` (or subclass) instance is a multiple of this constant.
605+
| `MethodDescAlignment` | `MethodDescChunk` trailing data is allocated in multiples of this constant. The size (in bytes) of each `MethodDesc` (or subclass) instance is a multiple of this constant. |
606+
| `MethodDescTokenRemainderBitCount` | Number of bits in the token remainder in `MethodDesc` |
567607

568608

569609
In the runtime a `MethodDesc` implicitly belongs to a single `MethodDescChunk` and some common data is shared between method descriptors that belong to the same chunk. A single method table
@@ -572,12 +612,211 @@ will typically have multiple chunks. There are subkinds of MethodDescs at runti
572612
We depend on the following data descriptors:
573613
| Data Descriptor Name | Field | Meaning |
574614
| --- | --- | --- |
575-
| `MethodDesc` | `ChunkIndex` | Offset of this `MethodDesc` relative to the end of its containing `MethodDescChunk` - in multiples of `MethodDescAlignment`
576-
| `MethodDesc` | `Slot` | The method's slot
577-
| `MethodDesc` | `Flags` | The method's flags
578-
| `MethodDescChunk` | `MethodTable` | The method table set of methods belongs to
579-
| `MethodDescChunk` | `Next` | The next chunk of methods
580-
| `MethodDescChunk` | `Size` | The size of this `MethodDescChunk` following this `MethodDescChunk` header, minus 1. In multiples of `MethodDescAlignment`
581-
| `MethodDescChunk` | `Count` | The number of `MethodDesc` entries in this chunk, minus 1.
615+
| `MethodDesc` | `ChunkIndex` | Offset of this `MethodDesc` relative to the end of its containing `MethodDescChunk` - in multiples of `MethodDescAlignment` |
616+
| `MethodDesc` | `Slot` | The method's slot |
617+
| `MethodDesc` | `Flags` | The method's flags |
618+
| `MethodDesc` | `Flags3AndTokenRemainder` | More flags for the method, and the low bits of the method's token's RID |
619+
| `MethodDescChunk` | `MethodTable` | The method table set of methods belongs to |
620+
| `MethodDescChunk` | `Next` | The next chunk of methods |
621+
| `MethodDescChunk` | `Size` | The size of this `MethodDescChunk` following this `MethodDescChunk` header, minus 1. In multiples of `MethodDescAlignment` |
622+
| `MethodDescChunk` | `Count` | The number of `MethodDesc` entries in this chunk, minus 1. |
623+
| `MethodDescChunk` | `FlagsAndTokenRange` | `MethodDescChunk` flags, and the upper bits of the method token's RID |
624+
| `InstantiatedMethodDesc` | `PerInstInfo` | The pointer to the method's type arguments |
625+
| `InstantiatedMethodDesc` | `Flags2` | Flags for the `InstantiatedMethodDesc` |
626+
| `InstantiatedMethodDesc` | `NumGenericArgs` | How many generic args the method has |
627+
| `StoredSigMethodDesc` | `Sig` | Pointer to a metadata signature |
628+
| `StoredSigMethodDesc` | `cSig` | Count of bytes in the metadata signature |
629+
| `StoredSigMethodDesc` | `ExtendedFlags` | Flags field for the `StoredSigMethodDesc` |
630+
| `DynamicMethodDesc` | `MethodName` | Pointer to Null-terminated UTF8 string describing the Method desc |
631+
632+
633+
And the following enumeration definitions
634+
635+
```csharp
636+
internal enum MethodDescClassification
637+
{
638+
IL = 0, // IL
639+
FCall = 1, // FCall (also includes tlbimped ctor, Delegate ctor)
640+
PInvoke = 2, // PInvoke method
641+
EEImpl = 3, // special method; implementation provided by EE (like Delegate Invoke)
642+
Array = 4, // Array ECall
643+
Instantiated = 5, // Instantiated generic methods, including descriptors
644+
// for both shared and unshared code (see InstantiatedMethodDesc)
645+
ComInterop = 6,
646+
Dynamic = 7, // for method desc with no metadata behind
647+
}
648+
649+
[Flags]
650+
internal enum MethodDescFlags : ushort
651+
{
652+
ClassificationMask = 0x7,
653+
HasNonVtableSlot = 0x0008,
654+
}
655+
656+
internal enum InstantiatedMethodDescFlags2 : ushort
657+
{
658+
KindMask = 0x07,
659+
GenericMethodDefinition = 0x01,
660+
UnsharedMethodInstantiation = 0x02,
661+
SharedMethodInstantiation = 0x03,
662+
WrapperStubWithInstantiations = 0x04,
663+
}
664+
665+
[Flags]
666+
internal enum DynamicMethodDescExtendedFlags : uint
667+
{
668+
IsLCGMethod = 0x00004000,
669+
IsILStub = 0x00008000,
670+
}
671+
```
672+
673+
674+
And the various apis are implemented with the following algorithms
675+
676+
```csharp
677+
public bool IsGenericMethodDefinition(MethodDescHandle methodDescHandle)
678+
{
679+
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
680+
681+
if (methodDesc.Classification != MethodDescClassification.Instantiated)
682+
return false;
683+
684+
ushort Flags2 = // Read Flags2 field from InstantiatedMethodDesc contract using address methodDescHandle.Address
685+
686+
return ((int)Flags2 & (int)InstantiatedMethodDescFlags2.KindMask) == (int)InstantiatedMethodDescFlags2.GenericMethodDefinition;
687+
}
688+
689+
public ReadOnlySpan<TypeHandle> GetGenericMethodInstantiation(MethodDescHandle methodDescHandle)
690+
{
691+
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
692+
693+
if (methodDesc.Classification != MethodDescClassification.Instantiated)
694+
return default;
695+
696+
TargetPointer dictionaryPointer = // Read PerInstInfo field from InstantiatedMethodDesc contract using address methodDescHandle.Address
697+
if (dictionaryPointer == 0)
698+
return default;
699+
700+
int NumTypeArgs = // Read NumGenericArgs from methodDescHandle.Address using InstantiatedMethodDesc contract
701+
TypeHandle[] instantiation = new TypeHandle[NumTypeArgs];
702+
for (int i = 0; i < NumTypeArgs; i++)
703+
instantiation[i] = GetTypeHandle(_target.ReadPointer(dictionaryPointer + _target.PointerSize * i));
704+
705+
return instantiation;
706+
}
707+
708+
public uint GetMethodToken(MethodDescHandle methodDescHandle)
709+
{
710+
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
711+
712+
TargetPointer methodDescChunk = // Using ChunkIndex from methodDesc, compute the wrapping MethodDescChunk
713+
714+
ushort Flags3AndTokenRemainder = // Read Flags3AndTokenRemainder field from MethodDesc contract using address methodDescHandle.Address
715+
716+
ushort FlagsAndTokenRange = // Read FlagsAndTokenRange field from MethodDescChunk contract using address methodDescChunk
582717
718+
int tokenRemainderBitCount = _target.ReadGlobal<byte>(Constants.Globals.MethodDescTokenRemainderBitCount);
719+
int tokenRangeBitCount = 24 - tokenRemainderBitCount;
720+
uint allRidBitsSet = 0xFFFFFF;
721+
uint tokenRemainderMask = allRidBitsSet >> tokenRangeBitCount;
722+
uint tokenRangeMask = allRidBitsSet >> tokenRemainderBitCount;
723+
724+
uint tokenRemainder = (uint)(_desc.Flags3AndTokenRemainder & tokenRemainderMask);
725+
uint tokenRange = ((uint)(_chunk.FlagsAndTokenRange & tokenRangeMask)) << tokenRemainderBitCount;
726+
727+
return 0x06000000 | tokenRange | tokenRemainder;
728+
}
729+
730+
public bool IsArrayMethod(MethodDescHandle methodDescHandle, out ArrayFunctionType functionType)
731+
{
732+
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
733+
734+
if (methodDesc.Classification != MethodDescClassification.Array)
735+
{
736+
functionType = default;
737+
return false;
738+
}
739+
740+
int arrayMethodIndex = methodDesc.Slot - GetNumVtableSlots(GetTypeHandle(methodDesc.MethodTable));
741+
742+
functionType = arrayMethodIndex switch
743+
{
744+
0 => ArrayFunctionType.Get,
745+
1 => ArrayFunctionType.Set,
746+
2 => ArrayFunctionType.Address,
747+
> 3 => ArrayFunctionType.Constructor,
748+
_ => throw new InvalidOperationException()
749+
};
750+
751+
return true;
752+
}
753+
754+
public bool IsNoMetadataMethod(MethodDescHandle methodDescHandle, out ReadOnlySpan<byte> methodName)
755+
{
756+
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
757+
758+
if (methodDesc.Classification != MethodDescClassification.Dynamic)
759+
{
760+
methodName = default;
761+
return false;
762+
}
763+
764+
TargetPointer methodNamePointer = // Read MethodName field from DynamicMethodDesc contract using address methodDescHandle.Address
765+
766+
methodName = // ReadBuffer from target of a utf8 null terminated string, starting at address methodNamePointer
767+
return true;
768+
}
769+
770+
public bool IsStoredSigMethodDesc(MethodDescHandle methodDescHandle, out ReadOnlySpan<byte> signature)
771+
{
772+
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
773+
774+
switch (methodDesc.Classification)
775+
{
776+
case MethodDescClassification.Dynamic:
777+
case MethodDescClassification.EEImpl:
778+
case MethodDescClassification.Array:
779+
break; // These have stored sigs
780+
781+
default:
782+
signature = default;
783+
return false;
784+
}
785+
786+
TargetPointer Sig = // Read Sig field from StoredSigMethodDesc contract using address methodDescHandle.Address
787+
uint cSig = // Read cSig field from StoredSigMethodDesc contract using address methodDescHandle.Address
788+
789+
TargetPointer methodNamePointer = // Read S field from DynamicMethodDesc contract using address methodDescHandle.Address
790+
signature = // Read buffer from target memory starting at address Sig, with cSig bytes in it.
791+
return true;
792+
}
793+
794+
public bool IsDynamicMethod(MethodDescHandle methodDescHandle)
795+
{
796+
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
797+
798+
if (methodDesc.Classification != MethodDescClassification.Dynamic)
799+
{
800+
return false;
801+
}
802+
803+
uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address
804+
805+
return ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsLCGMethod);
806+
}
807+
808+
public bool IsILStub(MethodDescHandle methodDescHandle)
809+
{
810+
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
811+
812+
if (methodDesc.Classification != MethodDescClassification.Dynamic)
813+
{
814+
return false;
815+
}
816+
817+
uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address
818+
819+
return ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsILStub);
820+
}
821+
```
583822
**TODO(cdac)**

src/coreclr/debug/daccess/dacfn.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,15 @@ DacAllocHostOnlyInstance(ULONG32 size, bool throwEx)
13861386
return inst + 1;
13871387
}
13881388

1389+
thread_local bool t_DacAssertsUnconditionally = false;
1390+
1391+
bool DacSetEnableDacAssertsUnconditionally(bool enable)
1392+
{
1393+
bool oldValue = t_DacAssertsUnconditionally;
1394+
t_DacAssertsUnconditionally = enable;
1395+
return oldValue;
1396+
}
1397+
13891398
//
13901399
// Queries whether ASSERTs should be raised when inconsistencies in the target are detected
13911400
//
@@ -1404,6 +1413,10 @@ bool DacTargetConsistencyAssertsEnabled()
14041413
return true;
14051414
}
14061415

1416+
// If asserts are unconditionally enabled via the thread local, simply return true.
1417+
if (t_DacAssertsUnconditionally)
1418+
return true;
1419+
14071420
return g_dacImpl->TargetConsistencyAssertsEnabled();
14081421
}
14091422

src/coreclr/debug/daccess/dacimpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,6 +1245,7 @@ class ClrDataAccess
12451245
HRESULT GetObjectStringDataImpl(CLRDATA_ADDRESS obj, unsigned int count, _Inout_updates_z_(count) WCHAR *stringData, unsigned int *pNeeded);
12461246
HRESULT GetUsefulGlobalsImpl(struct DacpUsefulGlobalsData *globalsData);
12471247
HRESULT GetMethodDescDataImpl(CLRDATA_ADDRESS methodDesc, CLRDATA_ADDRESS ip, struct DacpMethodDescData *data, ULONG cRevertedRejitVersions, DacpReJitData * rgRevertedRejitData, ULONG * pcNeededRevertedRejitData);
1248+
HRESULT GetMethodDescNameImpl(CLRDATA_ADDRESS methodDesc, unsigned int count, _Inout_updates_z_(count) WCHAR *name, unsigned int *pNeeded);
12481249

12491250
BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord);
12501251
#ifndef TARGET_UNIX

0 commit comments

Comments
 (0)