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

Native AOT CLI Experience Improvements in .NET 8 #79570

Closed
2 of 3 tasks
Tracked by #69739
agocke opened this issue Dec 12, 2022 · 11 comments
Closed
2 of 3 tasks
Tracked by #69739

Native AOT CLI Experience Improvements in .NET 8 #79570

agocke opened this issue Dec 12, 2022 · 11 comments
Labels
area-NativeAOT-coreclr User Story A single user-facing feature. Can be grouped under an epic.
Milestone

Comments

@agocke
Copy link
Member

agocke commented Dec 12, 2022

The Native AOT CLI (SDK) experience has a lot of rough edges and room for improvement.

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Dec 12, 2022
@agocke agocke added area-NativeAOT-coreclr and removed untriaged New issue has not been triaged by the area owner labels Dec 12, 2022
@ghost
Copy link

ghost commented Dec 12, 2022

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

Issue Details

The Native AOT CLI (SDK) experience has a lot of rough edges and room for improvement.

Author: agocke
Assignees: -
Labels:

area-NativeAOT-coreclr

Milestone: -

@agocke agocke added this to the 8.0.0 milestone Dec 12, 2022
@agocke agocke added the User Story A single user-facing feature. Can be grouped under an epic. label Dec 12, 2022
@agocke agocke changed the title Improve Native AOT CLI Experience Native AOT CLI Experience Improvements in .NET 8 Dec 12, 2022
@Xylobol
Copy link

Xylobol commented Dec 19, 2022

Sorry to bump this with a few people on it, but I'm interested in helping with this. I have a few projects I want to work on that depend on building native libraries from C# code, but the experience has been frustrating. I'm not very experienced with "compiler stuff", nor have I landed changes in .NET, but I'd love to learn way more about both, and this seems like a great opportunity to do so. If you've got time, please send some pointers for where work can be done on these.

@agocke
Copy link
Member Author

agocke commented Dec 19, 2022

@Xylobol The issues linked in there might be a good place to get started, but I'd love to make your experience better.

Maybe you could try writing down what you wanted to and what you expected the experience to be, then the problems with it? Writing native libraries is a pretty new thing for the dotnet toolchain so I expect there are some rough edges, but don't have a good sense of what people will run into.

@Xylobol
Copy link

Xylobol commented Dec 20, 2022

I'm trying to get a "native" shared library that I can dynamically load from a C/C++ program running on Linux. The generation itself seems to be fine on the .NET side - I can inspect the compiled library and see my methods. I haven't been able to consume the library though - per #70277, there's a ton of extra things to link when building. A complete example using CMake would be very helpful.

@Xylobol
Copy link

Xylobol commented Dec 25, 2022

I got it working and prepared an example solution over at https://github.com/Xylobol/DotNet-NativeAOT-Library-Example. I'm likely going to add extra information on writing headers and calling from C/C++ later, maybe in addition to how to deal with static libraries.

EDIT 12/24/22, a few minutes after posting: A source generator for header files would be killer

@Xylobol
Copy link

Xylobol commented Dec 25, 2022

A few more questions:

  • How do I handle a callback passed in by native code? e.g. I have an exported C# method void DoSomething(SomeType callback) and native code wants to give it a callback
  • Any reason I can't use byte[]?

Merry Christmas or whatever you all may celebrate, and happy New Year!

@Xylobol
Copy link

Xylobol commented Dec 25, 2022

In the new Christmas tradition of answering my own questions: for any other explorers, use delegates for the first part of that last post!

In the exporting lib:

public static partial class Class1
{
    // Callback function which is called from the unmanaged code
    [UnmanagedCallersOnly]
    private static void TestCallback()
    {
        Console.WriteLine("Hello from the test callback");
    }
    
    [UnmanagedCallersOnly(EntryPoint = "GetFunctionPointer", CallConvs = new[] { typeof(CallConvCdecl) })]
    private static unsafe delegate* unmanaged<void> GetFunctionPointer()
    {
        return &TestCallback;
    }
}

In the program you're calling from, in this case also written in C#:

public static partial class Class1
{
    private delegate void TestDelegate();
    
    [LibraryImport("whatever.dll", EntryPoint = "GetFunctionPointer")]
    [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
    private static partial nint GetFunctionPointer();

    private static readonly TestDelegate Delegate = Marshal.GetDelegateForFunctionPointer<TestDelegate>(GetFunctionPointer());

    public static void RunTheFunction() => Delegate();
}

A lot of this seems like documentation that assumes you already know what you're doing - I'd love to help with that, whether that's writing or just giving input to someone else who is.

@jkotas
Copy link
Member

jkotas commented Dec 25, 2022

    private static nint GetFunctionPointer()
    {
        var testDelegate = new TestDelegate(TestCallback);
        return Marshal.GetFunctionPointerForDelegate(testDelegate);
    }

This method has "callback on collected delegate" bug that will cause it to crash intermittently. Marshal.GetFunctionPointerForDelegate documentation
says "You must manually keep the delegate from being collected by the garbage collector from managed code. The garbage collector does not track references to unmanaged code.". It is possible to fix the bug by keeping the delegate alive as the documentation suggests, however it is better to use function pointers here instead. The library code rewritten using function pointers would look like this:

public static partial class Class1
{
    private delegate void TestDelegate();
    
    //Callback function which is called from the unmanaged code
    [UnmanagedCallersOnly]
    private static void TestCallback()
    {
        Console.WriteLine("Hello from the test callback");
    }
    
    [UnmanagedCallersOnly(EntryPoint = "GetFunctionPointer", CallConvs = new[] { typeof(CallConvCdecl) })]
    public static unsafe delegate* unmanaged<void> GetFunctionPointerNative() => &TestCallback;
}

Similarly, you can use function pointers for the calling code as well.

@Xylobol
Copy link

Xylobol commented Dec 25, 2022

Wow, that's really clean - thanks for showing me! I'll update my example soon.

@agocke agocke mentioned this issue Feb 14, 2023
6 tasks
@MichalStrehovsky
Copy link
Member

I pushed static library publishing to .NET 9. We don't currently have plans to encourage people to do static libraries. It will be more realistic if we do #83611 - that one avoids the issue of "you need to match the toolset that was used to compile the unmanaged parts of the runtime or bad things might happen".

There's nothing else left here, so closing.

@dotnet dotnet locked as resolved and limited conversation to collaborators Aug 14, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-NativeAOT-coreclr User Story A single user-facing feature. Can be grouped under an epic.
Projects
Archived in project
Development

No branches or pull requests

4 participants