Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cannot import/export a component interface from dotnet.wasm to wasmtime #113868

Open
YuriArthas opened this issue Mar 25, 2025 · 4 comments
Open
Labels
arch-wasm WebAssembly architecture area-Interop-mono needs-author-action An issue or pull request that requires more info or actions from the author. os-wasi Related to WASI variant of arch-wasm
Milestone

Comments

@YuriArthas
Copy link

Description

Since .NET 9, wasi-wasm defaults to generating a component.

In component mode, I cannot export functions using UnmanagedCallersOnly (this can only be exported in the Core Module, not at the Component Level). However, because export statements are generated in the Core Module, I can forcibly export functions at the Component level by modifying the wat, which barely solves the problem—even though it’s rather inelegant.

However, I still cannot import a function. If I use:
[DllImport("example:component/im", EntryPoint = "add5678")]
this will not generate any import statements in the wat. If I add WasmImportLinkage, a linking error occurs: _failed to resolve import example:component/im::add5678_

My goal is to have the wasmtime rust API define the function add5678, rather than having it defined by another Component wasm.

I hope there is a way to generat a Core Module instead of a Component. Then I can choose whether to manually generate a Component using wasm-tools component new or find another way to handle import/export functions for the host.

It's difficult for me to use

dotnet add package Microsoft.DotNet.ILCompiler.LLVM --prerelease
dotnet add package runtime.win-x64.Microsoft.DotNet.ILCompiler.LLVM --prerelease

because my use case includes iOS, and iOS doesn't support AOT. (I noticed that the last version of runtime.iossimulator-x64.Microsoft.DotNet.ILCompiler was released on 2023/7/11, so I believe it is no longer usable.)

Reproduction Steps

// Project6.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <RootNamespace>project6</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
    <UseAppHost>false</UseAppHost>
    <PublishTrimmed>true</PublishTrimmed>
    <InvariantGlobalization>true</InvariantGlobalization>
    <SelfContained>true</SelfContained>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    <SelfContained>true</SelfContained>

  </PropertyGroup>

</Project>
// Program.cs

using ExampleWorld.wit.imports.example.component;

namespace ExampleWorld{
public class ExampleWorldImpl: IExampleWorld{
    public static uint Add1234(uint a, uint b){
        var c = ImInterop.Add5678(a, b);
        Console.WriteLine($"Add5678: {c}");
        return c + 1;
    }



    public static void Main(string[] args) {
        Console.WriteLine($"Add1234: {Add1234(3, 4)}");
    }
};
}
// w.wit
package example:component;

interface im{
    add5678: func(x: u32, y: u32) -> u32;
}

world example {
    import im;
    export add1234: func(x: u32, y: u32) -> u32;
}
// Examples.cs  (generated by w.wit)
// Generated by `wit-bindgen` 0.39.0. DO NOT EDIT!
// <auto-generated />
#nullable enable
using System.Runtime.InteropServices;
namespace ExampleWorld {

    public interface IExampleWorld {
        static abstract uint Add1234(uint x, uint y);

    }

    public readonly struct None {}

    [StructLayout(LayoutKind.Sequential)]
    public readonly struct Result<TOk, TErr>
    {
        public readonly byte Tag;
        private readonly object value;

        private Result(byte tag, object value)
        {
            Tag = tag;
            this.value = value;
        }

        public static Result<TOk, TErr> Ok(TOk ok)
        {
            return new Result<TOk, TErr>(Tags.Ok, ok!);
        }

        public static Result<TOk, TErr> Err(TErr err)
        {
            return new Result<TOk, TErr>(Tags.Err, err!);
        }

        public bool IsOk => Tag == Tags.Ok;
        public bool IsErr => Tag == Tags.Err;

        public TOk AsOk
        {
            get
            {
                if (Tag == Tags.Ok)
                {
                    return (TOk)value;
                }

                throw new ArgumentException("expected k, got " + Tag);
            }
        }

        public TErr AsErr
        {
            get
            {
                if (Tag == Tags.Err)
                {
                    return (TErr)value;
                }

                throw new ArgumentException("expected Err, got " + Tag);
            }
        }

        public class Tags
        {
            public const byte Ok = 0;
            public const byte Err = 1;
        }
    }

    namespace exports {
        using System.Runtime.InteropServices;
        public static class ExampleWorld
        {

            [UnmanagedCallersOnly(EntryPoint = "add1234")]
            public static unsafe int wasmExportAdd1234(int p0, int p1) {

                uint ret;
                ret = ExampleWorldImpl.Add1234((unchecked((uint)(p0))), (unchecked((uint)(p1))));
                return unchecked((int)(ret));

            }

            [UnmanagedCallersOnly(EntryPoint = "cabi_post_add1234")]
            public static void cabi_post_wasmExportAdd1234(int returnValue) {
                Console.WriteLine("TODO: cabi_post_add1234");
            }

        }
    }

}
// ExampleWorld.wit.imports.example.component.ImInterop.cs  (generated by w.wit)
// Generated by `wit-bindgen` 0.39.0. DO NOT EDIT!
// <auto-generated />
#nullable enable

using System.Runtime.InteropServices;

namespace ExampleWorld.wit.imports.example.component
{
    public static class ImInterop {

        internal static class Add5678WasmInterop
        {
            [DllImport("example:component/im", EntryPoint = "add5678"), WasmImportLinkage]
            internal static extern int wasmImportAdd5678(int p0, int p1);
        }

        public  static unsafe uint Add5678(uint x, uint y)
        {
            var result =  Add5678WasmInterop.wasmImportAdd5678(unchecked((int)(x)), unchecked((int)(y)));
            return unchecked((uint)(result));

        }

    }
}

Expected behavior

I hope dotnet build -c Release can compile successfully and generate dotnet.wasm.

Actual behavior

EXEC : error : failed to encode component

  Caused by:
      0: failed to decode world from module
      1: module was not valid
      2: failed to resolve import `example:component/im::add5678`
      3: module requires an import interface named `example:component/im`
  clang : error : linker command failed with exit code 1 (use -v to see invocation)

And, if I remove the WasmImportLinkage, dotnet build will be success, but cannot found any import ... add5678 from wasm-tools print .\bin\Release\net10.0\wasi-wasm\AppBundle\dotnet.wasm

Regression?

In the era of generating Core Modules, import/export was possible.

Known Workarounds

No response

Configuration

.Net version: .net 10 preview1
OS: windows
architecture: x64

discuss for wasi-wasm

Other information

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Mar 25, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Mar 25, 2025
@YuriArthas YuriArthas changed the title cannot import a component interface from wasmtime to dotnet.wasm cannot import/export a component interface from wasmtime to dotnet.wasm Mar 25, 2025
@YuriArthas YuriArthas changed the title cannot import/export a component interface from wasmtime to dotnet.wasm cannot import/export a component interface from dotnet.wasm to wasmtime Mar 25, 2025
@MichalStrehovsky MichalStrehovsky added the arch-wasm WebAssembly architecture label Mar 25, 2025
Copy link
Contributor

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

@jeffschwMSFT jeffschwMSFT added os-wasi Related to WASI variant of arch-wasm area-Interop-mono labels Mar 25, 2025
@agocke agocke added this to the Future milestone Mar 25, 2025
@agocke agocke removed the untriaged New issue has not been triaged by the area owner label Mar 25, 2025
@vcsjones vcsjones removed the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Mar 26, 2025
@maraf
Copy link
Member

maraf commented Mar 27, 2025

@pavelsavara
Copy link
Member

Since .NET 9, wasi-wasm defaults to generating a component.

Latest version of .Net9 patch disabled any WASI output, because WASI is not supported product. If you want to work with experimental version, tracking latest dotnet preview or LLVM experimental brach is way to go.

I hope there is a way to generate a Core Module instead of a Component.

We are currently targeting WASIp2 and wish to target WASIp3. WASI SDK doesn't produce core modules for that target.

My goal is to have the wasmtime rust API define the function add5678, rather than having it defined by another Component wasm.

This is punching holes into security boundary that components bring. Also the "component" that you would produce this way would not be compatible component anymore.

Could you please explain why do you want to avoid making it component model import ?

@pavelsavara pavelsavara added the needs-author-action An issue or pull request that requires more info or actions from the author. label Mar 27, 2025
Copy link
Contributor

This issue has been marked needs-author-action and may be missing some important information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
arch-wasm WebAssembly architecture area-Interop-mono needs-author-action An issue or pull request that requires more info or actions from the author. os-wasi Related to WASI variant of arch-wasm
Projects
Status: No status
Development

No branches or pull requests

7 participants