dotnet / dotnet/runtime

[wasm] reverse P/Invoke thunk key drops enclosing type for nested [UnmanagedCallersOnly] methods

Open
#130,739 6 comments 0 reactions 1 assignee Claimed by @radekdoulik View on GitHub
arch-wasm area-CodeGen-Interpreter-coreclr
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

## Summary

The CoreCLR wasm reverse-P/Invoke thunk table is keyed by
`{MethodName}#{argCount}:{AssemblyName}:{Namespace}:{TypeName}`. The key is produced on two sides that must agree:

- **Generator** — `src/tasks/WasmAppBuilder/coreclr/PInvokeCollector.cs`
- **Runtime lookup** — `src/coreclr/vm/wasm/helpers.cpp` (`GetHashCode` → `MethodTable::GetFullyQualifiedNameInfo` → `GetNameOfTypeDef`), which reads the namespace straight from ECMA-335 metadata.

For a **nested** type the metadata namespace is empty, and the key uses only the **simple** nested type name (the enclosing type chain is dropped on both sides). So two genuinely-distinct nested `[UnmanagedCallersOnly]` callbacks that share the same simple type name, method name, argument count, and assembly — but differ only by enclosing type/namespace — collapse to the **same key**, e.g.:

```
A.Outer+Callback.M -> M#0:MyAsm::Callback
B.Outer+Callback.M -> M#0:MyAsm::Callback // collides
```

## Current mitigation

The generator detects duplicate keys and fails the build loudly
(`PInvokeTableGenerator.cs`: *"Two callbacks with the same Name and number of arguments … are not supported"*), so this is a **build-time error, never a silent mis-dispatch**.

A recent fix (matching the generator's namespace to the runtime's metadata truth — empty namespace for nested types) made the nested-type key agree with the runtime lookup so nested `[UnmanagedCallersOnly]` callbacks resolve at all; before that they silently missed the lookup and asserted at `precode_portable.cpp:35`. See the CoreCLR-WASI library-test bring-up (#130129).

## Why this is acceptable for now

This name-hash reverse-thunk table is a wasm-interpreter bring-up mechanism and is expected to be **replaced once R2R can generate the reverse thunks directly**. Until then, the build-time collision error is sufficient — a program hitting it is rare and gets a clear diagnostic rather than incorrect behavior.

## Possible robust fix (if the generator persists)

Include the full enclosing-type chain in the key on **both** sides (generator + runtime `GetHashCode`) so nested types are fully disambiguated, and add a CoreCLR nested-type `[UnmanagedCallersOnly]` test (there is currently no nested-type coverage; `UnmanagedCallbackNamespaced` covers only top-level types on the Mono path).

> [!NOTE]
> This issue was filed with the assistance of GitHub Copilot.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.