Skip to content

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

@JamesMenetrey

Description

@JamesMenetrey

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions