Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ff70d24

Browse files
author
Max Charlamb
committed
doc wip
1 parent 8fd4e4d commit ff70d24

File tree

7 files changed

+130
-36
lines changed

7 files changed

+130
-36
lines changed

docs/design/datacontracts/ECall.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Contract ECall
2+
3+
This contract is for fetching information related to native calls into the runtime.
4+
5+
## APIs of contract
6+
7+
``` csharp
8+
// Given an FCall entrypoint returns the corresponding MethodDesc.
9+
// If the address does not correspond to an FCall, returns TargetPointer.Null.
10+
TargetPointer MapTargetBackToMethodDesc(TargetCodePointer address);
11+
```
12+
13+
## Version 1
14+
15+
Global variables used
16+
| Global Name | Type | Purpose |
17+
| --- | --- | --- |
18+
| FCallMethods | ECHash[] | Hash table containing ECHash structures |
19+
| FCallHashSize | uint | Number of buckets in the hash table |
20+
21+
22+
Data descriptors used:
23+
| Data Descriptor Name | Field | Meaning |
24+
| --- | --- | --- |
25+
| `ECHash` | `Next` | Pointer to the next ECHash in the chain |
26+
| `ECHash` | `Implementation` | FCall's Entrypoint address |
27+
| `ECHash` | `MethodDesc` | Pointer to the FCall's method desc |
28+
29+
30+
``` csharp
31+
TargetPointer IECall.MapTargetBackToMethodDesc(TargetCodePointer codePointer)
32+
```
33+
34+
To map an FCall entrypoint back to a MethodDesc, we read the global `FCallMethods` hash table. This is a array of pointers to `ECHash` objects. The length of this array is defined by the global `FCallHashSize` and it uses a simple hash function: `<hash> = codePointer % FCallHashSize`. To find the correct `ECHash` chain, simply take the `<hash>` offset into the `FCallMethods` array.
35+
36+
Once we find the `ECHash` chain, we iterate the chain until we find an `ECHash` where the `Implementation` field matches the `codePointer`. If found, return the `MethodDesc` field.
37+
38+
If no `ECHash` matches return `TargetPointer.Null` to indicate a MethodDesc was not found.

docs/design/datacontracts/Loader.md

+81
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ TargetPointer GetAssembly(ModuleHandle handle);
5959
TargetPointer GetPEAssembly(ModuleHandle handle);
6060
bool TryGetLoadedImageContents(ModuleHandle handle, out TargetPointer baseAddress, out uint size, out uint imageFlags);
6161
bool TryGetSymbolStream(ModuleHandle handle, out TargetPointer buffer, out uint size);
62+
List<TargetPointer> GetAvailableTypeParams(ModuleHandle handle);
63+
List<TargetPointer> GetInstantiatedMethods(ModuleHandle handle);
64+
6265
bool IsProbeExtensionResultValid(ModuleHandle handle);
6366
ModuleFlags GetFlags(ModuleHandle handle);
6467
string GetPath(ModuleHandle handle);
@@ -86,6 +89,8 @@ bool IsAssemblyLoaded(ModuleHandle handle);
8689
| `Module` | `Path` | Path of the Module (UTF-16, null-terminated) |
8790
| `Module` | `FileName` | File name of the Module (UTF-16, null-terminated) |
8891
| `Module` | `GrowableSymbolStream` | Pointer to the in memory symbol stream |
92+
| `Module` | `AvailableTypeParams` | Pointer to an EETypeHashTable |
93+
| `Module` | `InstMethodHashTable` | Pointer to an InstMethodHashTable |
8994
| `Module` | `FieldDefToDescMap` | Mapping table |
9095
| `Module` | `ManifestModuleReferencesMap` | Mapping table |
9196
| `Module` | `MemberRefToDescMap` | Mapping table |
@@ -118,6 +123,14 @@ bool IsAssemblyLoaded(ModuleHandle handle);
118123
| `ArrayListBlock` | `Next` | Next ArrayListBlock in chain |
119124
| `ArrayListBlock` | `Size` | Size of data section in block |
120125
| `ArrayListBlock` | `ArrayStart` | Start of data section in block |
126+
| `EETypeHashTable` | `Buckets` | Pointer to hash table buckets |
127+
| `EETypeHashTable` | `Count` | Count of elements in the hash table |
128+
| `EETypeHashTable` | `VolatileEntryValue` | The data stored in the hash table entry |
129+
| `EETypeHashTable` | `VolatileEntryNextEntry` | Next pointer in the hash table entry |
130+
| `InstMethodHashTable` | `Buckets` | Pointer to hash table buckets |
131+
| `InstMethodHashTable` | `Count` | Count of elements in the hash table |
132+
| `InstMethodHashTable` | `VolatileEntryValue` | The data stored in the hash table entry |
133+
| `InstMethodHashTable` | `VolatileEntryNextEntry` | Next pointer in the hash table entry |
121134

122135

123136
### Global variables used:
@@ -298,6 +311,38 @@ bool TryGetSymbolStream(ModuleHandle handle, out TargetPointer buffer, out uint
298311
return true;
299312
}
300313

314+
List<TargetPointer> GetAvailableTypeParams(ModuleHandle handle)
315+
{
316+
TargetPointer availableTypeParams = target.ReadPointer(handle.Address + /* Module::AvailableTypeParams offset */);
317+
318+
if (availableTypeParams == TargetPointer.Null) return [];
319+
320+
// EETypeHashTable is read as a DacEnumerableHash table.
321+
// For more information on how this is read, see below.
322+
EETypeHashTable typeHashTable = // read EETypeHashTable at availableTypeParams
323+
324+
return [.. typeHashTable.Entries];
325+
}
326+
327+
List<TargetPointer> GetInstantiatedMethods(ModuleHandle handle)
328+
{
329+
TargetPointer instMethodHashTable = target.ReadPointer(handle.Address + /* Module::InstMethodHashTable offset */);
330+
331+
if (instMethodHashTable == TargetPointer.Null) return [];
332+
333+
// InstMethodHashTable is read as a DacEnumerableHash table.
334+
// For more information on how this is read, see below.
335+
InstMethodHashTable typeHashTable = // read InstMethodHashTable at instMethodHashTable
336+
337+
List<TargetPointer> typeParams = []
338+
foreach (TargetPointer methodDescPtrWithFlags in methodHashTable.Entires)
339+
{
340+
typeParams.Add(methodDescPtrWithFlags & ~0x3ul);
341+
}
342+
343+
return typeParams;
344+
}
345+
301346
bool IsProbeExtensionResultValid(ModuleHandle handle)
302347
{
303348
TargetPointer peAssembly = target.ReadPointer(handle.Address + /* Module::PEAssembly offset */);
@@ -415,3 +460,39 @@ bool ILoader.IsAssemblyLoaded(ModuleHandle handle)
415460
return assembly.Level >= ASSEMBLY_LEVEL_LOADED;
416461
}
417462
```
463+
464+
### DacEnumerableHash (EETypeHashTable and InstMethodHashTable)
465+
466+
Both `EETypeHashTable` and `InstMethodHashTable` are based on the templated `DacEnumerableHash`. Because the base class is templated on the derived type, offsets may be different in derived types.
467+
468+
The base implementation of `DacEnumerableHash` uses four datadescriptors:
469+
| Datadescriptor | Purpose |
470+
| --- | --- |
471+
| `Buckets` | Pointer to the bucket array |
472+
| `Count` | Number of elements in the hash table |
473+
| `VolatileEntryValue` | The data held by an entry, defined by the derived class |
474+
| `VolatileEntryNextEntry` | The next pointer on an hash table entry |
475+
476+
The hash table is laid out as an array of `VolatileEntry` pointers's (buckets), each possibly forming a chain for values that hash into that bucket. The first three buckets are special and reserved for special metadata. Instead of containing a `VolatileEntry`, these pointers are read as values with the following meanings.
477+
478+
| Reserved Bucket offset | Purpose |
479+
| --- | --- |
480+
| `0` | Length of the Bucket array, this value does not include the first 3 slots which are special |
481+
| `1` | Pointer to the next bucket array, not currently used in the cDAC |
482+
| `2` | End sentinel for the current bucket array, not currently used in the cDAC |
483+
484+
To read all entries in the hash table:
485+
1. Read the length bucket to find the number of chains `n`.
486+
2. Initialize a list of elements `entries = []`.
487+
3. For each chain, (buckets with offsets `3..n + 3`):
488+
1. Read the pointer in the bucket as `volatileEntryPtr`.
489+
2. If `volatileEntryPtr & 0x1 == 0x1`, this is the end sentinel and we stop reading this chain.
490+
3. Otherwise, add `volatileEntryPtr + /* VolatileEntryValue offset */` to entries. This points to the derived class defined data type.
491+
4. Set `volatileEntryPtr` to the value of the pointer located at `volatileEntryPtr + /* VolatileEntryNextEntry offset */` and go to step 3.2.
492+
4. Return `entries` to be further parsed by derived classes.
493+
494+
For both EETypeHashTable and InstMethodHashTable, the data type stored on the `VolatileEntry` are simply pointers with a couple of bits used as flags.
495+
496+
EETypeHashTable uses the LSB to indicate if the TypeHandle is a hot entry. Therefore this bit is stripped before values are returned.
497+
498+
InstMethodHashTable uses the 2 most LSBs as flags for the MethodDesc. These must be taken into account before reading values from them.

docs/design/datacontracts/RuntimeTypeSystem.md

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ partial interface IRuntimeTypeSystem : IContract
4141
public virtual TargetPointer GetCanonicalMethodTable(TypeHandle typeHandle);
4242
public virtual TargetPointer GetParentMethodTable(TypeHandle typeHandle);
4343

44+
public virtual TargetPointer GetMethodDescForSlot(TypeHandle typeHandle, ushort slot);
45+
4446
public virtual uint GetBaseSize(TypeHandle typeHandle);
4547
// 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)
4648
public virtual uint GetComponentSize(TypeHandle typeHandle);

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,13 @@ public interface ILoader : IContract
6666
TargetPointer GetPEAssembly(ModuleHandle handle) => throw new NotImplementedException();
6767
bool TryGetLoadedImageContents(ModuleHandle handle, out TargetPointer baseAddress, out uint size, out uint imageFlags) => throw new NotImplementedException();
6868
bool TryGetSymbolStream(ModuleHandle handle, out TargetPointer buffer, out uint size) => throw new NotImplementedException();
69+
List<TargetPointer> GetAvailableTypeParams(ModuleHandle handle) => throw new NotImplementedException();
70+
List<TargetPointer> GetInstantiatedMethods(ModuleHandle handle) => throw new NotImplementedException();
71+
6972
bool IsProbeExtensionResultValid(ModuleHandle handle) => throw new NotImplementedException();
7073
ModuleFlags GetFlags(ModuleHandle handle) => throw new NotImplementedException();
7174
string GetPath(ModuleHandle handle) => throw new NotImplementedException();
7275
string GetFileName(ModuleHandle handle) => throw new NotImplementedException();
73-
List<TargetPointer> GetAvailableTypeParams(ModuleHandle handle) => throw new NotImplementedException();
74-
List<TargetPointer> GetInstantiatedMethods(ModuleHandle handle) => throw new NotImplementedException();
75-
7676
TargetPointer GetLoaderAllocator(ModuleHandle handle) => throw new NotImplementedException();
7777
TargetPointer GetILBase(ModuleHandle handle) => throw new NotImplementedException();
7878
ModuleLookupTables GetLookupTables(ModuleHandle handle) => throw new NotImplementedException();

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

+3-4
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,8 @@ List<TargetPointer> ILoader.GetAvailableTypeParams(ModuleHandle handle)
180180
return typeParams;
181181

182182
EETypeHashTable typeHashTable = _target.ProcessedData.GetOrAdd<EETypeHashTable>(module.AvailableTypeParams);
183-
typeParams.AddRange(typeHashTable.Entries);
184183

185-
return typeParams;
184+
return [.. typeHashTable.Entries];
186185
}
187186

188187
List<TargetPointer> ILoader.GetInstantiatedMethods(ModuleHandle handle)
@@ -195,9 +194,9 @@ List<TargetPointer> ILoader.GetInstantiatedMethods(ModuleHandle handle)
195194

196195
InstMethodHashTable methodHashTable = _target.ProcessedData.GetOrAdd<InstMethodHashTable>(module.InstMethodHashTable);
197196

198-
foreach (InstMethodHashTable.InstMethodHashTableEntry entry in methodHashTable.Entries)
197+
foreach (TargetPointer methodDescPtrWithFlags in methodHashTable.Entries)
199198
{
200-
typeParams.Add(entry.MethodDesc);
199+
typeParams.Add(methodDescPtrWithFlags & ~0x3ul);
201200
}
202201

203202
return typeParams;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/DacEnumerableHash.cs

-8
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ namespace Microsoft.Diagnostics.DataContractReader.Data;
1717
internal sealed class DacEnumerableHash
1818
{
1919
private const int SLOT_LENGTH = 0;
20-
// private const int SLOT_NIEXT = 1;
21-
private const int SLOT_ENDSENTINEL = 2;
2220
private const int SKIP_SPECIAL_SLOTS = 3;
2321

2422
private readonly Target _target;
@@ -76,12 +74,6 @@ private uint GetLength()
7674
return (uint)length;
7775
}
7876

79-
private TargetPointer GetBaseSentinel()
80-
{
81-
// Second pointer is a size_t base sentinel
82-
TargetPointer baseSentinel = _target.ReadPointer(Buckets + (ulong)(SLOT_ENDSENTINEL * _target.PointerSize));
83-
return baseSentinel;
84-
}
8577

8678
private static bool IsEndSentinel(TargetPointer value)
8779
{

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/InstMethodHashTable.cs

+3-21
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,14 @@ public InstMethodHashTable(Target target, TargetPointer address)
1616

1717
DacEnumerableHash baseHashTable = new(target, address, type);
1818

19-
List<InstMethodHashTableEntry> entries = [];
19+
List<TargetPointer> entries = [];
2020
foreach (TargetPointer entry in baseHashTable.Entries)
2121
{
2222
TargetPointer methodDescPtr = target.ReadPointer(entry);
23-
InstMethodHashTableEntry instMethodHashTableEntry = new()
24-
{
25-
MethodDesc = methodDescPtr.Value & ~0x3ul,
26-
Flags = (InstMethodHashTableFlags)(methodDescPtr.Value & 0x3ul)
27-
};
28-
entries.Add(instMethodHashTableEntry);
23+
entries.Add(methodDescPtr);
2924
}
3025
Entries = entries;
3126
}
3227

33-
public IReadOnlyList<InstMethodHashTableEntry> Entries { get; init; }
34-
35-
public readonly struct InstMethodHashTableEntry
36-
{
37-
public TargetPointer MethodDesc { get; init; }
38-
public InstMethodHashTableFlags Flags { get; init; }
39-
}
40-
41-
[Flags]
42-
public enum InstMethodHashTableFlags
43-
{
44-
UnboxingStub = 0x1,
45-
RequiresInstArg = 0x2,
46-
}
28+
public IReadOnlyList<TargetPointer> Entries { get; init; }
4729
}

0 commit comments

Comments
 (0)