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

[NativeAOT] Decorating functions with UnmanagedCallersOnlyAttribute does not export them for executables #78663

Closed
JamesMenetrey opened this issue Nov 21, 2022 · 7 comments
Labels
area-NativeAOT-coreclr help wanted [up-for-grabs] Good issue for external contributors
Milestone

Comments

@JamesMenetrey
Copy link
Contributor

JamesMenetrey commented Nov 21, 2022

Introduction

NativeAOT conveniently enables Class Library projects to export native symbols in the export table of the PE, typically functions, which is an excellent asset for interoperability with unmanaged software.

In native applications (e.g., written in C/C++), using the directive extern __declspec(dllexport) similarly creates an entry in the export table for the given function. This capability works for both unmanaged libraries (.dll) and unmanaged executables (.exe).

Currently, when decorating a function with the attribute UnmanagedCallersOnlyAttrribute does not insert a symbol in the export table when the managed project is an application (.exe). Copy/paste the definition of the same function in a project of type Class Library works.

Motivation

I'm looking forward to similarly exporting functions in the export table of my managed applications (.exe) for interoperability reasons.
Admittedly, the use case of exporting functions for executables is small compared to libraries. Nonetheless, I find this is a valuable capability on my end, where I mainly work on low-level forms of interoperability (i.e., creating threads in other processes for code execution).

Furthermore, I would expect the compiler to honor the attribute UnmanagedCallersOnlyAttribute for exporting the function in the export table since both libraries (.dll) and executables (.exe) use the PE file format, which enables the export of symbol of functions.

Bug report

Version Used: Retail .NET SDK 7.0.100 with Visual Studio 17.4.0

Steps to Reproduce:

  1. Create a Console Application application using the default template in Visual Studio 2022.
  2. Write a function decorated with the attribute, e.g.:
[UnmanagedCallersOnly(EntryPoint = "FancyName2")]
public static int FancyName2(int a, int b)
{
    return a + b;
}
  1. Compile with the application for win-x64 using NativeAOT.
  2. Inspect the export table of the resulting executable, for example, using CFF Explorer.

Expected Behavior:

The export table of the PE format shows the FancyName2 function.

Actual Behavior

The function FancyName2 is not present. Note that performing the same operation for a project of type Class Library correctly exports the function.

Additional details

Inspecting a running application compiled using NativeAOT, I could notice various symbols present in the memory of the process. As an example, the picture below highlights a static function called FancyName2 in the class Program, from a Windows Forms application.

image

Creating a thread at this address properly invokes the managed function. Hence, I think tagging this location in the export table using the same logic as for managed libraries should be doable.

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Nov 21, 2022
@jkoritzinsky jkoritzinsky transferred this issue from dotnet/roslyn Nov 21, 2022
@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
Copy link

ghost commented Nov 21, 2022

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

Issue Details

Introduction

NativeAOT conveniently enables Class Library projects to export native symbols in the export table of the PE, typically functions, which is an excellent asset for interoperability with unmanaged software.

In native applications (e.g., written in C/C++), using the directive extern __declspec(dllexport) similarly creates an entry in the export table for the given function. This capability works for both unmanaged libraries (.dll) and unmanaged executables (.exe).

Currently, when decorating a function with the attribute UnmanagedCallersOnlyAttrribute does not insert a symbol in the export table when the managed project is an application (.exe). Copy/paste the definition of the same function in a project of type Class Library works.

Motivation

I'm looking forward to similarly exporting functions in the export table of my managed applications (.exe) for interoperability reasons.
Admittedly, the use case of exporting functions for executables is small compared to libraries. Nonetheless, I find this is a valuable capability on my end, where I mainly work on low-level forms of interoperability (i.e., creating threads in other processes for code execution).

Furthermore, I would expect the compiler to honor the attribute UnmanagedCallersOnlyAttribute for exporting the function in the export table since both libraries (.dll) and executables (.exe) use the PE file format, which enables the export of symbol of functions.

Bug report

Version Used: Retail .NET SDK 7.0.100 with Visual Studio 17.4.0

Steps to Reproduce:

  1. Create a Windows Forms application using the default template in Visual Studio 2022.
  2. Write a function decorated with the attribute, e.g.:
[UnmanagedCallersOnly(EntryPoint = "FancyName2")]
public static int FancyName2(int a, int b)
{
    return a + b;
}
  1. Compile with the application for win-x64 using NativeAOT.
  2. Inspect the export table of the resulting executable, for example, using CFF Explorer.

Expected Behavior:

The export table of the PE format shows the FancyName2 function.

Actual Behavior

The function FancyName2 is not present. Note that performing the same operation for a project of type Class Library correctly exports the function.

Additional details

Inspecting a running application compiled using NativeAOT, I could notice various symbols present in the memory of the process. As an example, the picture below highlights a static function called FancyName2 in the class Program, from a Windows Forms application.

image

Creating a thread at this address properly invokes the managed function. Hence, I think tagging this location in the export table using the same logic as for managed libraries should be doable.

Author: JamesMenetrey
Assignees: -
Labels:

untriaged, area-NativeAOT-coreclr

Milestone: -

@agocke agocke added this to the Future milestone Nov 22, 2022
@agocke agocke removed the untriaged New issue has not been triaged by the area owner label Nov 22, 2022
@agocke
Copy link
Member

agocke commented Nov 22, 2022

Seems reasonable, but a little niche. Would take a PR if there were one ready.

@jkotas
Copy link
Member

jkotas commented Nov 22, 2022

I agree that this is niche. You can work around this by passing the .def file with the list of entrypoints to the linker yourself.

I expect that most uses of UnmanagedCallersOnlyAttribute for interopability would not want the entrypoints to be automatically exported in .exe. If the option for dllexporting is added, it should be off by default.

@MichalStrehovsky
Copy link
Member

I expect that most uses of UnmanagedCallersOnlyAttribute for interopability would not want the entrypoints to be automatically exported in .exe. If the option for dllexporting is added, it should be off by default.

I think the request is to only expose those that specify the EntryPoint property - the uses for interoperability wouldn't specify an EntryPoint.

@JamesMenetrey you can already do this today if you add:

    <ExportsFile>$(MSBuildThisFileDirectory)exports.def</ExportsFile>

To a PropertyGroup in your project file. It's a bit of a backhanded way to exploit the way things are set up in the targets.

The actual product change to fix this in a more supportable way would be a change to the targets in https://github.com/dotnet/runtime/tree/main/src/coreclr/nativeaot/BuildIntegration.

@MichalStrehovsky MichalStrehovsky added the help wanted [up-for-grabs] Good issue for external contributors label Nov 22, 2022
@jkotas
Copy link
Member

jkotas commented Nov 22, 2022

the uses for interoperability wouldn't specify an EntryPoint.

Consider statically linking some extra C code into the exe and calling between C# and C in both directions.

JamesMenetrey added a commit to JamesMenetrey/runtime that referenced this issue Nov 22, 2022
Functions decorated with the attribute UnmanagedCallersOnlyAttribute
are not inserted in the export table of the compiled file when the
.NET project is an executable (i.e., as opposed to a library).
This change removes the difference between a library and an executable
regarding the handling of the attribute UnmanagedCallersOnlyAttribute.
As such, decorated functions with this attribute are now adequately
exported for libraries and executables.

Fix dotnet#78663
@JamesMenetrey
Copy link
Contributor Author

Dear all,

First, thanks for your detailed input and insight that allowed me to understand the underlying mechanism works better.

@MichalStrehovsky Many thanks for pointing me to a working workaround. Adding this line in the project file correctly exports the symbols as annotated with UnmanagedCallersOnlyAttribute for executables.

I have reviewed the many files in the link you provided to me and created a PR fixing the issue (#78738).

After further testing with both a .NET library and two .NET executables (a console and Windows Forms application), I noticed that decorating a function with UnmanagedCallersOnlyAttribute without specifying the property EntryPoint does not export the symbol. This behavior goes in the direction of @jkotas, who stated that the usage of UnmanagedCallersOnlyAttribute should not export that symbol by default.

Filling the property EntryPoint adds a symbol for both libraries and executables once the PR is applied (tested on my Windows machine).

Could you please review my PR (#78738) and provide feedback if this change is sound?

Thanks!

@ghost ghost locked as resolved and limited conversation to collaborators Dec 24, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-NativeAOT-coreclr help wanted [up-for-grabs] Good issue for external contributors
Projects
Archived in project
Development

No branches or pull requests

5 participants