Skip to content

[release/11.0] Fix NativeAOT thread-static debug info - #133144

Merged
steveisok merged 1 commit into
release/11.0from
backport/pr-133109-to-release/11.0
Sep 8, 2026
Merged

steveisok merged 1 commit into
release/11.0from
backport/pr-133109-to-release/11.0

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Backport of #133109 to release/11.0

/cc @max-charlamb

Customer Impact

  • Customer reported
  • Found internally

[Select one or both of the boxes. Describe how this issue impacts customers, citing the expected and actual behaviors and scope of the issue. If customer-reported, provide the issue number.]

Regression

  • Yes
  • No

[If yes, specify when the regression was introduced. Provide the PR or commit if known.]

Testing

[How was the fix verified? How was the issue missed previously? What tests were added?]

Risk

[High/Medium/Low. Justify the indication by mentioning how risks were measured and addressed.]

IMPORTANT: If this backport is for a servicing release, please verify that:

  • For .NET 8 and .NET 9: The PR target branch is release/X.0-staging, not release/X.0.
  • For .NET 10+: The PR target branch is release/X.0 (no -staging suffix).

Package authoring no longer needed in .NET 9

IMPORTANT: Starting with .NET 9, you no longer need to edit a NuGet package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older versions.

## Summary

- Treat thread-static, GC-static, and non-GC-static fields as mutually
exclusive storage categories when emitting NativeAOT debug types.
- Preserve thread-static fields when the owning type has a generated
thread-static index but no separately generated ordinary GC-static base.

## Problem

`FieldDesc.HasGCStaticBase` returns `true` for NativeAOT thread-static
fields. The debug type writer first accepted a thread-static field based
on `HasThreadStaticBase`, but then independently tested the same field
as an ordinary GC-static field:

```csharp
if (fieldDesc.IsThreadStatic && !hasThreadStatics)
    continue;

if (fieldDesc.HasGCStaticBase)
{
    if (!hasGcStatics)
        continue;
}
```

This dropped all thread-static fields from types that had no separately
generated GC-static base. An example is the `AsyncDispatcherInfo` ref
struct:

```csharp
internal unsafe ref struct AsyncDispatcherInfo
{
    // Instance fields omitted.

    [ThreadStatic]
    internal static AsyncDispatcherInfo* t_current;
}
```

The index symbol was emitted because the type had thread-static storage,
but the PDB omitted the typed parent member, thread-static storage UDT,
helper UDT, and `t_current` field offset.

The fix makes the three storage categories mutually exclusive by
changing the second test to `else if`.

## CDB validation

I built a small runtime-async NativeAOT application that roots
`AsyncDispatcherInfo` and inspected its Windows PDB with CDB.

Before the fix, the index existed only as an untyped symbol. The parent
type had no `__THREADSTATICINDEX`, and the thread-static storage type
did not exist:

```text
0:000> x AsyncDispatcherPdb!S_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo::__THREADSTATICINDEX
00007ff7`cd8a7ed8 AsyncDispatcherPdb!S_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo::__THREADSTATICINDEX = <no type information>

0:000> dt AsyncDispatcherPdb!S_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo
   +0x000 Next             : Ptr64 S_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo
   +0x008 NextContinuation : Ptr64 S_P_CoreLib_System_Runtime_CompilerServices_Continuation
   +0x010 CurrentTask      : Ptr64 S_P_CoreLib_System_Threading_Tasks_Task
   +0x018 AsyncProfilerInfo : S_P_CoreLib_System_Runtime_CompilerServices_AsyncProfiler_Info

0:000> dt AsyncDispatcherPdb!__type__THREADSTATICSS_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo
Symbol AsyncDispatcherPdb!__type__THREADSTATICSS_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo not found.
```

After the fix, the parent UDT has a typed `__THREADSTATICINDEX`, the
storage UDT contains `t_current` at offset `+0x008`, and the helper UDT
is present:

```text
0:000> dt AsyncDispatcherPdb!S_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo
   +0x000 Next             : Ptr64 S_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo
   +0x008 NextContinuation : Ptr64 S_P_CoreLib_System_Runtime_CompilerServices_Continuation
   +0x010 CurrentTask      : Ptr64 S_P_CoreLib_System_Threading_Tasks_Task
   +0x018 AsyncProfilerInfo : S_P_CoreLib_System_Runtime_CompilerServices_AsyncProfiler_Info
   =00007ff6`a5957ed8 __THREADSTATICINDEX : __ThreadStaticHelper<__type__THREADSTATICSS_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo>

0:000> dt AsyncDispatcherPdb!__type__THREADSTATICSS_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo
   +0x000 __VFN_table      : Ptr64
   +0x000 m_pEEType        : Ptr64 S_P_CoreLib_Internal_Runtime_MethodTable
   +0x008 t_current        : Ptr64 S_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo

0:000> dt AsyncDispatcherPdb!__ThreadStaticHelper*AsyncDispatcherInfo*
          AsyncDispatcherPdb!__ThreadStaticHelper<__type__THREADSTATICSS_P_CoreLib_System_Runtime_CompilerServices_AsyncDispatcherInfo>
```

## Testing

```text
ILCompiler.Compiler.Tests: Passed 22, Failed 0, Skipped 0
```

> [!NOTE]
> This PR description was generated with GitHub Copilot.

Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Copilot-Session: 32bd0ba5-1c4a-4c99-bdd4-969279117e18
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.

@steveisok steveisok added the Servicing-approved Approved for servicing release label Sep 8, 2026
@steveisok

Copy link
Copy Markdown
Member

/ba-g Unrelated known issue

@steveisok
steveisok enabled auto-merge (squash) September 8, 2026 17:41
@steveisok
steveisok merged commit d9ab8c7 into release/11.0 Sep 8, 2026
110 of 112 checks passed
@steveisok
steveisok deleted the backport/pr-133109-to-release/11.0 branch September 8, 2026 17:41
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-rc2 milestone Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-NativeAOT-coreclr Servicing-approved Approved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants