Skip to content

Commit 2214b67

Browse files
authored
Merge branch 'main' into opt-out-pooled
2 parents 214d6c5 + e0bfb35 commit 2214b67

845 files changed

Lines changed: 24626 additions & 12722 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/design/coreclr/botr/readytorun-format.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,36 @@ which encodes an extra 4-byte representing the end RVA of the unwind info blob.
413413
| 4 | 4 | Unwind info end RVA (1 plus RVA of last byte)
414414
| 8 | 4 | GC info start RVA
415415

416+
### RUNTIME_FUNCTION (wasm, size = 8 bytes)
417+
418+
On WebAssembly, the `RUNTIME_FUNCTION` uses a virtual IP as the `BeginAddress` rather than an RVA
419+
into the image. The high bit of the `BeginAddress` field indicates whether the entry represents a
420+
funclet (1) or a main method body (0). The remaining 31 bits encode the virtual IP of the start of
421+
the function or funclet.
422+
423+
| Offset | Size | Value
424+
|-------:|-----:|:-----
425+
| 0 | 4 | Virtual IP (bits 30:0) | IsFunclet flag (bit 31)
426+
| 4 | 4 | UnwindData RVA (GC info follows immediately after the unwind blob)
427+
428+
The table is terminated by a sentinel entry with all bits set (`0xFFFFFFFF`), followed by a 4-byte
429+
value containing the minimum WebAssembly function table index for the image.
430+
431+
### UnwindInfo (wasm)
432+
433+
On WebAssembly, the unwind info blob associated with each `RUNTIME_FUNCTION` entry is encoded as
434+
two consecutive ULEB128 values:
435+
436+
| Order | Encoding | Value
437+
|------:|:---------|:-----
438+
| 1 | ULEB128 | Frame size in bytes (the number of bytes to unwind from the stack)
439+
| 2 | ULEB128 | Virtual IP count divided by 2 (the number of virtual IPs logically present in the function, halved)
440+
441+
The virtual IP count (after multiplying by 2) gives the span of virtual IPs covered by this
442+
function or funclet. All virtual IPs are forced to even numbers so that the runtime can force all virtual
443+
ips to have odd numbers and fit into the address space in a manner which cannot conflict with either interpreter
444+
IPs or PortableEntryPoint structures.
445+
416446
## ReadyToRunSectionType.MethodDefEntryPoints
417447

418448
This section contains a native format sparse array (see 4 Native Format) that maps methoddef rows to
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Contract RuntimeMutableTypeSystem
2+
3+
This contract exposes runtime type system information about changes that occurred after the initial type load, such as from EnC or HotReload.
4+
5+
## APIs of contract
6+
7+
```csharp
8+
IEnumerable<TargetPointer> EnumerateAddedFieldDescs(TypeHandle typeHandle, bool staticFields);
9+
bool IsFieldDescEnCNew(TargetPointer fieldDescPointer);
10+
```
11+
12+
## Version 1
13+
14+
### Data descriptors
15+
16+
| Data Descriptor Name | Field | Meaning |
17+
| --- | --- | --- |
18+
| `Module` | `EnCClassList` | Address of the embedded `CUnorderedArray` (whose `CUnorderedArrayBase` portion holds `Count`/`Table`) holding the module's `EnCEEClassData*` entries. Optional: only present when EditAndContinue is configured. |
19+
| `UnorderedArrayBase` | `Count` | Number of valid entries currently stored in the array. |
20+
| `UnorderedArrayBase` | `Table` | Pointer to the backing storage holding the array's entries. |
21+
| `EnCEEClassData` | `MethodTable` | Pointer to the `MethodTable` whose EnC data is held by this entry. |
22+
| `EnCEEClassData` | `AddedInstanceFields` | Head of the linked list of `EnCAddedFieldElement` for added instance fields. |
23+
| `EnCEEClassData` | `AddedStaticFields` | Head of the linked list of `EnCAddedFieldElement` for added static fields. |
24+
| `EnCAddedFieldElement` | `Next` | Pointer to the next `EnCAddedFieldElement` in the linked list. |
25+
| `EnCAddedFieldElement` | `FieldDesc` | Address of the embedded `EnCFieldDesc` (layout-compatible with `FieldDesc`). |
26+
| `FieldDesc` | `DWord2` | Packed flags/offset word containing the field's offset; the EnC-new sentinel `FieldOffsetNewEnc` is stored here for fields that have been added but do not yet have storage assigned. |
27+
28+
### Globals used
29+
30+
| Global Name | Type | Purpose |
31+
| --- | --- | --- |
32+
| `FieldOffsetNewEnc` | uint | Sentinel offset value stored in `FieldDesc::DWord2` for added fields whose storage has not yet been allocated. |
33+
34+
### Required contracts
35+
36+
| Contract |
37+
| --- |
38+
| `IRuntimeTypeSystem` |
39+
| `ILoader` |
40+
41+
```csharp
42+
internal enum FieldDescFlags2 : uint
43+
{
44+
OffsetMask = 0x07ffffff,
45+
}
46+
47+
IEnumerable<TargetPointer> EnumerateAddedFieldDescs(TypeHandle typeHandle, bool staticFields)
48+
{
49+
// get modulePtr and moduleHandle from typeHandle
50+
// if there is no EnC data, yield break
51+
TargetPointer classList = modulePtr + /* Module::EnCClassList offset */;
52+
uint classListCount = target.Read<uint>(classList + /* UnorderedArrayBase::Count offset */);
53+
TargetPointer classListTable = target.ReadPointer(classList + /* UnorderedArrayBase::Table offset */);
54+
TargetPointer classDataPtr = TargetPointer.Null;
55+
56+
for (uint i = 0; i < classListCount; i++)
57+
{
58+
// search on EnC data for data that matches the method table
59+
TargetPointer entry = target.ReadPointer(classListTable + i * target.PointerSize);
60+
TargetPointer mt = target.ReadPointer(entry + /* EnCEEClassData::MethodTable offset */);
61+
if (mt == typeHandle.Address)
62+
{
63+
classDataPtr = entry;
64+
break;
65+
}
66+
}
67+
68+
// enumerate fields that have been added
69+
TargetPointer node = staticFields ? target.ReadPointer(classDataPtr + /* EnCEEClassData::AddedStaticFields offset */)
70+
: target.ReadPointer(classDataPtr + /* EnCEEClassData::AddedInstanceFields offset */);
71+
while (node != TargetPointer.Null)
72+
{
73+
yield return node + /* EnCAddedFieldElement::FieldDesc offset */;
74+
node = target.ReadPointer(node + /* EnCAddedFieldElement::Next offset */);
75+
}
76+
}
77+
78+
bool IsFieldDescEnCNew(TargetPointer fieldDescPointer)
79+
{
80+
uint DWord2 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord2 offset */);
81+
uint offset = DWord2 & (uint)FieldDescFlags2.OffsetMask;
82+
return offset == target.ReadGlobal<uint>("FieldOffsetNewEnc");
83+
}
84+
```

docs/design/datacontracts/RuntimeTypeSystem.md

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ partial interface IRuntimeTypeSystem : IContract
5151
public virtual TargetCodePointer GetSlot(TypeHandle typeHandle, uint slot);
5252

5353
public virtual uint GetBaseSize(TypeHandle typeHandle);
54+
// The number of bytes of instance fields stored in an object of this type on the GC heap.
55+
// Equivalent to the native MethodTable::GetNumInstanceFieldBytes(), which is computed as
56+
// GetBaseSize() minus the EEClass base-size padding (the trailing alignment/min-object-size
57+
// bytes included in BaseSize but not occupied by actual instance fields).
58+
public virtual uint GetNumInstanceFieldBytes(TypeHandle typeHandle);
5459
// 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)
5560
public virtual uint GetComponentSize(TypeHandle typeHandle);
5661

@@ -276,8 +281,9 @@ TargetPointer GetMTOfEnclosingClass(TargetPointer fieldDescPointer);
276281
uint GetFieldDescMemberDef(TargetPointer fieldDescPointer);
277282
bool IsFieldDescThreadStatic(TargetPointer fieldDescPointer);
278283
bool IsFieldDescStatic(TargetPointer fieldDescPointer);
284+
bool IsFieldDescRVA(TargetPointer fieldDescPointer);
279285
uint GetFieldDescType(TargetPointer fieldDescPointer);
280-
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition fieldDef);
286+
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition? fieldDef);
281287
TargetPointer GetFieldDescStaticAddress(TargetPointer fieldDescPointer, bool unboxValueTypes = true);
282288
TargetPointer GetFieldDescThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer thread, bool unboxValueTypes = true);
283289
```
@@ -581,6 +587,8 @@ Contracts used:
581587

582588
public uint GetBaseSize(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : _methodTables[TypeHandle.Address].Flags.BaseSize;
583589

590+
public uint GetNumInstanceFieldBytes(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : _methodTables[TypeHandle.Address].Flags.BaseSize - GetClassData(TypeHandle).BaseSizePadding;
591+
584592
public uint GetComponentSize(TypeHandle TypeHandle) =>!typeHandle.IsMethodTable() ? (uint)0 : GetComponentSize(_methodTables[TypeHandle.Address]);
585593

586594
public TargetPointer GetClassPointer(TypeHandle TypeHandle)
@@ -1189,7 +1197,7 @@ Contracts used:
11891197

11901198
### MethodDesc
11911199

1192-
The version 1 `MethodDesc` APIs depend on the following globals:
1200+
The `MethodDesc` APIs of RuntimeTypeSystem version 1 depend on the following globals:
11931201

11941202
| Global name | Meaning |
11951203
| --- | --- |
@@ -2072,7 +2080,14 @@ Getting a MethodDesc for a certain slot in a MethodTable
20722080

20732081
### FieldDesc
20742082

2075-
The version 1 FieldDesc APIs depend on the following data descriptors:
2083+
The FieldDesc APIs of RuntimeTypeSystem version 1 depend on the following globals:
2084+
2085+
| Global name | Meaning |
2086+
| --- | --- |
2087+
| `FieldOffsetBigRVA` | Sentinel value of `FieldDesc::DWord2` indicating the field is an RVA static whose offset is too large to encode in the bitfield; the real offset must be read from the field's metadata (`FieldDefinition.GetRelativeVirtualAddress`). |
2088+
| `FieldOffsetNewEnc` | Sentinel value of `FieldDesc`'s offset bitfield indicating the field was added via Edit-and-Continue and does not yet have backing storage. |
2089+
2090+
The FieldDesc APIs of RuntimeTypeSystem version 1 depend on the following data descriptors:
20762091
| Data Descriptor Name | Field | Meaning |
20772092
| --- | --- | --- |
20782093
| `FieldDesc` | `MTOfEnclosingClass` | Pointer to method table of enclosing class |
@@ -2085,6 +2100,7 @@ internal enum FieldDescFlags1 : uint
20852100
TokenMask = 0xffffff,
20862101
IsStatic = 0x1000000,
20872102
IsThreadStatic = 0x2000000,
2103+
IsRVA = 0x4000000,
20882104
}
20892105

20902106
internal enum FieldDescFlags2 : uint
@@ -2116,18 +2132,26 @@ bool IsFieldDescStatic(TargetPointer fieldDescPointer)
21162132
return (DWord1 & (uint)FieldDescFlags1.IsStatic) != 0;
21172133
}
21182134

2135+
bool IsFieldDescRVA(TargetPointer fieldDescPointer)
2136+
{
2137+
uint DWord1 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord1 offset */);
2138+
return (DWord1 & (uint)FieldDescFlags1.IsRVA) != 0;
2139+
}
2140+
21192141
uint GetFieldDescType(TargetPointer fieldDescPointer)
21202142
{
21212143
uint DWord2 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord2 offset */);
21222144
return (DWord2 & (uint)FieldDescFlags2.TypeMask) >> 27;
21232145
}
21242146

2125-
uint GetFieldDescOffset(TargetPointer fieldDescPointer)
2147+
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition? fieldDef)
21262148
{
21272149
uint DWord2 = target.Read<uint>(fieldDescPointer + /* FieldDesc::DWord2 offset */);
21282150
if (DWord2 == _target.ReadGlobal<uint>("FieldOffsetBigRVA"))
21292151
{
2130-
return (uint)fieldDef.GetRelativeVirtualAddress();
2152+
if (fieldDef is null)
2153+
throw new ArgumentNullException(nameof(fieldDef), "Field definition is required for big RVA fields");
2154+
return (uint)fieldDef.Value.GetRelativeVirtualAddress();
21312155
}
21322156
return DWord2 & (uint)FieldDescFlags2.OffsetMask;
21332157
}

docs/design/datacontracts/StackWalk.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ DebuggerEvalData GetDebuggerEvalData(TargetPointer funcEvalFrameAddress);
6767
// Walks the stack and returns all GC references found on each frame.
6868
// This is the primary API for GC reference enumeration, used by SOSDacImpl.GetStackReferences.
6969
IReadOnlyList<StackReferenceData> WalkStackReferences(ThreadData threadData);
70+
71+
// Returns a context for the thread, trying (in order): the debugger filter context,
72+
// the OS thread context, or a context derived from the explicit Frame chain.
73+
byte[] GetContext(ThreadData threadData, ThreadContextSource contextSource, uint contextFlags);
74+
75+
// Returns the saved TargetContext pointer carried by the head Frame, if applicable.
76+
TargetPointer GetRedirectedContextPointer(ThreadData threadData);
7077
```
7178

7279
## Version 1
@@ -577,6 +584,14 @@ IReadOnlyList<StackReferenceData> WalkStackReferences(ThreadData threadData)
577584

578585
The implementation uses the same stack walk algorithm as `CreateStackWalk`, but integrates the GC-aware `Filter` directly (rather than consuming pre-generated frames) and performs GC reference enumeration at each frame. See [GC Stack Reference Scanning](#gc-stack-reference-scanning) for details.
579586

587+
`GetContext` returns a thread context by trying three sources in order: (1) the debugger filter context from `ThreadData.DebuggerFilterContext` (when `ThreadContextSource.Debugger` is requested), (2) the OS thread context via `TryGetThreadContext`, or (3) a context derived from walking the explicit Frame chain (`Thread::Frame` linked list), returning the first frame that yields a usable context:
588+
* If the current Frame is an `InterpreterFrame`, clear the context and update it from the Frame. Return the resulting bytes.
589+
* Otherwise, clear the context and update it from the current Frame; accept the context when both the stack pointer and instruction pointer are non-zero (e.g. `RedirectedThreadFrame`, `InlinedCallFrame`, `DynamicHelperFrame`). Mark `RawContextFlags = FullContextFlags` so callers know SP/PC/FP are valid.
590+
591+
If no Frame in the chain produces a usable context (thread is not running managed code), a zeroed context of the target architecture's size is returned.
592+
593+
`GetRedirectedContextPointer` returns the saved `TargetContext` pointer carried by the head Frame when that Frame is a `RedirectedThreadFrame` (a `ResumableFrame`). Otherwise it returns `TargetPointer.Null`.
594+
580595
### GC Stack Reference Scanning
581596

582597
`WalkStackReferences` scans the stack for GC references by walking through each frame and reporting live object references and interior pointers. The native equivalent is `DacStackReferenceWalker` which calls `GcStackCrawlCallBack` at each frame.

docs/design/datacontracts/Thread.md

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ record struct ThreadData (
5353
bool HasUnhandledException;
5454
TargetPointer NextThread;
5555
TargetPointer ThreadHandle;
56+
bool IsInteropDebuggingHijacked;
57+
TargetPointer DebuggerFilterContext;
5658
);
5759
```
5860

@@ -74,7 +76,6 @@ void ResetDebuggerControlledThreadState(TargetPointer thread, DebuggerControlled
7476
void GetStackLimitData(TargetPointer threadPointer, out TargetPointer stackBase, out TargetPointer stackLimit, out TargetPointer frameAddress);
7577
TargetPointer IdToThread(uint id);
7678
TargetPointer GetThreadLocalStaticBase(TargetPointer threadPointer, TargetPointer tlsIndexPtr);
77-
byte[] GetContext(TargetPointer threadPointer, ThreadContextSource contextSource, uint contextFlags);
7879
```
7980

8081
## Version 1
@@ -131,6 +132,7 @@ The contract additionally depends on these data descriptors
131132
| `Thread` | `LinkNext` | Pointer to get next thread |
132133
| `Thread` | `ExceptionTracker` | Pointer to exception tracking information |
133134
| `Thread` | `DebuggerFilterContext` | Pointer to the debugger filter context for the thread |
135+
| `Thread` | `InteropDebuggingHijacked` | Whether the thread has been hijacked for interop debugging |
134136
| `Thread` | `RuntimeThreadLocals` | Pointer to some thread-local storage |
135137
| `Thread` | `ThreadLocalDataPtr` | Pointer to thread local data structure |
136138
| `Thread` | `ThreadHandle` | OS thread handle (optional, Windows only; readers should expect `TargetPointer.Null` on non-Windows targets) |
@@ -385,39 +387,4 @@ byte[] IThread.GetWatsonBuckets(TargetPointer threadPointer)
385387
return span.ToArray();
386388
}
387389

388-
byte[] IThread.GetContext(TargetPointer threadPointer, ThreadContextSource contextSource, uint contextFlags)
389-
{
390-
// Allocate a context buffer for the target platform
391-
IPlatformAgnosticContext context = IPlatformAgnosticContext.GetContextForPlatform(target);
392-
byte[] bytes = new byte[context.Size];
393-
394-
TargetPointer filterContext = TargetPointer.Null;
395-
396-
if (contextSource.HasFlag(ThreadContextSource.Debugger))
397-
filterContext = target.ReadPointer(threadPointer + /* Thread::DebuggerFilterContext offset */);
398-
399-
if (filterContext != TargetPointer.Null)
400-
{
401-
// Use the filter context directly
402-
target.ReadBuffer(filterContext, bytes);
403-
return bytes;
404-
}
405-
406-
// Try to read the OS thread context from the data target
407-
ulong osId = target.ReadNUInt(threadPointer + /* Thread::OSId offset */);
408-
if (target.TryGetThreadContext(osId, contextFlags, bytes))
409-
return bytes;
410-
411-
// Derive a context from the explicit Frame chain stored in the Thread (sufficient for managed
412-
// debugging stack walks). Walk the Frame chain and pick the first
413-
// frame that yields a usable context:
414-
// * If it is an InterpreterFrame, fill the context from the top
415-
// InterpMethodContextFrame.
416-
// * Otherwise, update the context from the frame and accept it when both
417-
// StackPointer and InstructionPointer are non-zero. Mark the resulting
418-
// context flags as full so callers know SP/PC/FP are valid.
419-
// If no frame supplies a usable context (thread is not running managed code),
420-
// return a zeroed context.
421-
}
422-
423390
```

docs/design/features/host-runtime-information.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ List of directory paths to search for managed assemblies. Paths are delimited by
7878

7979
List of directory paths corresponding to shared store paths and additional probing paths used by the host for [probing paths](./host-probing.md#probing-paths). Paths are delimited by a [platform-specific path separator](#path-separator).
8080

81+
`SYSTEM_CORELIB_DIRECTORY`
82+
83+
Absolute path to the directory containing `System.Private.CoreLib.dll`. When set, the runtime loads `System.Private.CoreLib` from this directory instead of looking for it beside `coreclr`. This is intended for hosts where `System.Private.CoreLib.dll` is not placed alongside `coreclr`. For hosts providing an assembly probe extension, a valid probe result takes precedence over this property. Otherwise, when this property is set, no fallback search is performed - if the file cannot be loaded from the specified directory, startup fails. Host is only allowed to specify directory since the runtime expects corelib to always be called `System.Private.CoreLib.dll`.
84+
8185
### Single-file
8286

8387
`BUNDLE_EXTRACTION_PATH`

docs/project/list-of-diagnostics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
274274
| __`SYSLIB1226`__ | 'JsonIgnoreCondition.Always' is not valid on type-level 'JsonIgnoreAttribute' annotations. |
275275
| __`SYSLIB1227`__ | Union case types cannot be unambiguously classified by JSON value type. |
276276
| __`SYSLIB1228`__ | Union type shape is not a valid C# union. |
277-
| __`SYSLIB1229`__ | _`SYSLIB1220`-`SYSLIB1229` reserved for System.Text.Json.SourceGeneration._ |
277+
| __`SYSLIB1229`__ | Open generic derived type could not be resolved for the polymorphic base type. |
278278
| __`SYSLIB1230`__ | Deriving from a `GeneratedComInterface`-attributed interface defined in another assembly is not supported. |
279279
| __`SYSLIB1231`__ | _`SYSLIB1230`-`SYSLIB1239` reserved for Microsoft.Interop.ComInterfaceGenerator._ |
280280
| __`SYSLIB1232`__ | _`SYSLIB1230`-`SYSLIB1239` reserved for Microsoft.Interop.ComInterfaceGenerator._ |

eng/AcquireWasiSdk.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
-->
88
<Project>
99
<PropertyGroup>
10-
<_WasiSdkVersion>25.0</_WasiSdkVersion>
10+
<_WasiSdkVersion>33.0</_WasiSdkVersion>
1111
<_RuntimeLocalWasiSdkPath>$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'wasi-sdk'))</_RuntimeLocalWasiSdkPath>
1212
</PropertyGroup>
1313

eng/Analyzers.targets

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,16 @@
2929
<EditorConfigFiles Remove="$(RepositoryEngineeringDir)CodeAnalysis.src.globalconfig" />
3030
<EditorConfigFiles Include="$(RepositoryEngineeringDir)CodeAnalysis.test.globalconfig" />
3131
</ItemGroup>
32+
33+
<!-- PlatformDocAnalyzer: Enforce documentation conventions for platform-specific libraries.
34+
Opt-in via EnablePlatformDocAnalyzer; src/libraries enables this by default for src projects. -->
35+
<ItemGroup Condition="'$(EnablePlatformDocAnalyzer)' == 'true' and '$(MSBuildProjectExtension)' == '.csproj' and '$(RunAnalyzers)' != 'false'">
36+
<ProjectReference Include="$(RepositoryEngineeringDir)analyzers\PlatformDocAnalyzer\PlatformDocAnalyzer.csproj"
37+
ReferenceOutputAssembly="false"
38+
OutputItemType="Analyzer"
39+
SetConfiguration="Configuration=$(LibrariesConfiguration)" />
40+
</ItemGroup>
41+
42+
<Import Project="$(RepositoryEngineeringDir)analyzers\PlatformDocAnalyzer\PlatformDocAnalyzer.props"
43+
Condition="'$(EnablePlatformDocAnalyzer)' == 'true' and '$(MSBuildProjectExtension)' == '.csproj' and '$(RunAnalyzers)' != 'false'" />
3244
</Project>

0 commit comments

Comments
 (0)