Skip to content

[wasm] Export cDAC contract descriptor getter on non-emscripten wasm (wasi-sdk)#131241

Open
lewing wants to merge 2 commits into
dotnet:mainfrom
lewing:wasi-cdac-descriptor-export
Open

[wasm] Export cDAC contract descriptor getter on non-emscripten wasm (wasi-sdk)#131241
lewing wants to merge 2 commits into
dotnet:mainfrom
lewing:wasi-cdac-descriptor-export

Conversation

@lewing

@lewing lewing commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

On non-emscripten wasm (wasi-sdk), the cDAC contract descriptor getter GetDotNetRuntimeContractDescriptor is not exported, so --gc-sections strips the descriptor from the final module. This prevents a native host / the cDAC reader from discovering the runtime data-contract descriptor on WASI.

The emscripten path already handles this via EMSCRIPTEN_KEEPALIVE, which both exports the getter and keeps the descriptor alive. There was no equivalent for the wasi-sdk (__wasm__, non-emscripten) toolchain.

Fix

Add an #elif defined(EXPORT_CONTRACT) && defined(__wasm__) branch that emits the getter with __attribute__((export_name("Get" #CONTRACT_NAME))). The explicit wasm export both makes the symbol reachable by a native host and roots it so --gc-sections does not remove the descriptor — mirroring what EMSCRIPTEN_KEEPALIVE does on the emscripten path.

The GETTER_NAME token-paste macros are hoisted above the #if (shared by both branches) and a GETTER_NAME_STR stringize pair is added to produce the literal export name "GetDotNetRuntimeContractDescriptor".

Validation

  • WASI CoreCLR build (clr -os wasi -c Release): compiles clean (0W/0E).
  • Confirmed the compiled object exports GetDotNetRuntimeContractDescriptor with the no_strip flag, so it survives --gc-sections.

This unblocks live cDAC discovery on WASI (non-emscripten) — the runtime-side counterpart to the cDAC-wasm validation work (#131161). Fixture-based cDAC tests do not depend on it.

Note

This PR was authored with the assistance of GitHub Copilot.

…(wasi-sdk)

On non-emscripten wasm (wasi-sdk), the cDAC contract descriptor getter
GetDotNetRuntimeContractDescriptor was not exported, so --gc-sections stripped
the descriptor from the final module, preventing a native host / the cDAC reader
from discovering the runtime data-contract descriptor on WASI. The emscripten
path already handles this via EMSCRIPTEN_KEEPALIVE.

Add an `#elif defined(EXPORT_CONTRACT) && defined(__wasm__)` branch that emits the
getter with __attribute__((export_name("Get" #CONTRACT_NAME))), which both exports
the symbol and roots it against --gc-sections. Hoist the GETTER_NAME token-paste
macros above the #if and add a GETTER_NAME_STR stringize pair shared by both branches.

Verified: WASI CoreCLR build (clr -os wasi -c Release) 0W/0E; the compiled object
exports GetDotNetRuntimeContractDescriptor with the no_strip flag.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2902374a-f352-4c46-bcec-8a35c97733ae
@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.

@lewing lewing added the arch-wasm WebAssembly architecture label Jul 22, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates CoreCLR’s shared cDAC contract-descriptor generator to ensure the contract descriptor getter is exported (and thus retained by the linker) when building for non-Emscripten WebAssembly toolchains (e.g., WASI via wasi-sdk).

Changes:

  • Hoists the getter-name token-paste macros so they can be shared across export implementations.
  • Adds a non-Emscripten WebAssembly export path using __attribute__((export_name(...))) to export the getter and keep the descriptor reachable under --gc-sections.

Comment thread src/coreclr/debug/datadescriptor-shared/contract-descriptor.c.in Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 22, 2026 23:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

@jkoritzinsky jkoritzinsky left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something we can unify between Emscripten and WASI? Or does Emscripten not support the WASI mechanism?

@lewing

lewing commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

Is this something we can unify between Emscripten and WASI? Or does Emscripten not support the WASI mechanism?

possibly, the emscripten version wires up some things automatically. I will check how it is consumed.

@lewing

lewing commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

They could share the export_name path at the wasm level — emscripten's clang supports the attribute — but I kept them split here on purpose, because the two toolchains surface the export differently and only the WASI side is validated in this PR:

  • WASI / wasi-sdk: consumers reach the descriptor via the raw wasm export. Validated object-level — the getter is emitted with no_strip and survives --gc-sections.
  • emscripten / browser: the descriptor is reached through emscripten's runtime glue — the cDAC discovery path calls getDotnetRuntime(0).INTERNAL.GetDotNetRuntimeContractDescriptor(), i.e. the getter surfaced into INTERNAL by emscripten's Module/JS generation (which EMSCRIPTEN_KEEPALIVE participates in, and which per the existing comment may also need EXPORTED_FUNCTIONS). Whether a bare export_name still lands in that INTERNAL bridge the same way is unvalidated, and there's reason to doubt it (raw wasm-ld export vs. emscripten's export convention; _Name naming asymmetry).

So unifying now would risk the one proven path (browser discovery) for no functional gain in a WASI-scoped change. I'd rather unify as a follow-up gated on an explicit browser cDAC re-validation: build an emscripten CoreCLR with the emscripten branch switched to bare export_name, run browser discovery, and confirm INTERNAL.GetDotNetRuntimeContractDescriptor() still returns the descriptor pointer. If it does → unify; if not → the split is load-bearing.

Note

This reply was drafted with the assistance of GitHub Copilot.

@lewing
lewing marked this pull request as ready for review July 23, 2026 03:47
@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants