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] Support exporting methods from referenced assemblies in a formal way #83396

Merged
merged 20 commits into from
Mar 22, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0897d10
[naot] Export methods from referenced assemblies
ivanpovazan Mar 13, 2023
ef7bf05
[naot] Adding test case
ivanpovazan Mar 14, 2023
baf20d6
[naot] Enable the feature for all configurations
ivanpovazan Mar 16, 2023
6383518
[naot] Introduce better naming
ivanpovazan Mar 16, 2023
230bfa9
[naot] Updating the test case based on the feedback
ivanpovazan Mar 16, 2023
b8ff7c7
[naot] Use the new feature to export SPC unmanaged entry points inste…
ivanpovazan Mar 16, 2023
9d73b20
[naot] Cleanup
ivanpovazan Mar 16, 2023
db91bb7
[naot] Given a more appropriate name for the unmanaged entry points r…
ivanpovazan Mar 16, 2023
18c0c59
[naot] Adjust repro.csproj to generate the new compiler switch
ivanpovazan Mar 16, 2023
0dab9ec
[naot] Skip adding System.Private.CoreLib as a UnmanagedEntryPointsRo…
ivanpovazan Mar 16, 2023
e9593b0
[naot] Test cleanup
ivanpovazan Mar 16, 2023
549ed66
[naot] Include exporting SPC entry points only if system module is no…
ivanpovazan Mar 17, 2023
dc45d4c
[naot] Switching the test to be a single executable
ivanpovazan Mar 17, 2023
ba51637
[naot] Match the ordering from .targets file
ivanpovazan Mar 17, 2023
ea71b8c
[naot] Explicitly export symbols from an executable via IlcExportUnma…
ivanpovazan Mar 17, 2023
87bac10
[naot] Enable overriding IlcExportUnmanagedEntrypoints
ivanpovazan Mar 17, 2023
5ddc135
[naot] Test cleanup
ivanpovazan Mar 17, 2023
a5a1c0a
[naot] Using more general approach to avoid adding the same module mu…
ivanpovazan Mar 17, 2023
cdbc605
[naot] Fix for including exported symbols in the dynamic symbol table…
ivanpovazan Mar 20, 2023
24a530e
[naot] Using a more appropriate condition
ivanpovazan Mar 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
<Target Name="_ComputeAssembliesToCompileToNative" DependsOnTargets="$(IlcDynamicBuildPropertyDependencies)">

<Warning Condition="Exists($(UserRuntimeConfig))" Text="The published project has a runtimeconfig.template.json that is not supported by PublishAot. Move the configuration to the project file using RuntimeHostConfigurationOption." />

<!-- Fail with descriptive error message for common mistake. -->
<Error Condition="$([MSBuild]::VersionLessThan('$(NETCoreSdkVersion)', '6.0.0'))" Text=".NET SDK 6+ is required for native compilation." />
<Error Condition="$([MSBuild]::VersionLessThan('$(TargetFrameworkVersion)', '6.0'))" Text="For native compilation, the project needs to target .NET 6 or higher." />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ The .NET Foundation licenses this file to you under the MIT license.
<_IlcConditionallyRootedAssemblies Include="@(ManagedAssemblyToLink)" Condition="%(ManagedAssemblyToLink.TrimMode) == 'copyused'" />
<_IlcTrimmedAssemblies Include="@(ManagedAssemblyToLink)" Condition="%(ManagedAssemblyToLink.TrimMode) == 'link'" />
<_IlcNoSingleWarnAssemblies Include="@(ManagedAssemblyToLink)" Condition="%(ManagedAssemblyToLink.TrimmerSingleWarn) == 'false'" />

<!-- always export unmanaged entry points from System.Private.CoreLib -->
<UnmanagedEntryPointsAssembly Include="System.Private.CoreLib" />
ivanpovazan marked this conversation as resolved.
Show resolved Hide resolved
ivanpovazan marked this conversation as resolved.
Show resolved Hide resolved
</ItemGroup>

<ItemGroup>
Expand Down Expand Up @@ -241,6 +244,7 @@ The .NET Foundation licenses this file to you under the MIT license.
<IlcArg Include="@(_IlcNoSingleWarnAssemblies->'--nosinglewarnassembly:%(Filename)')" />
<IlcArg Condition="'$(TrimmerDefaultAction)' == 'copyused' or '$(TrimmerDefaultAction)' == 'copy' or '$(TrimMode)' == 'partial'" Include="--defaultrooting" />
<IlcArg Include="--resilient" />
<IlcArg Include="@(UnmanagedEntryPointsAssembly->'--generateunmanagedentrypoints:%(Identity)')" />

<IlcArg Condition="$(IlcDisableReflection) == 'true'" Include="--feature:System.Reflection.IsReflectionExecutionAvailable=false" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ namespace ILCompiler
/// <summary>
/// Computes a set of roots based on managed and unmanaged methods exported from a module.
/// </summary>
public class ExportedMethodsRootProvider : ICompilationRootProvider
public class UnmanagedEntryPointsRootProvider : ICompilationRootProvider
{
private EcmaModule _module;

public ExportedMethodsRootProvider(EcmaModule module)
public UnmanagedEntryPointsRootProvider(EcmaModule module)
{
_module = module;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@
<Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64ReadyToRunHelperNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64ReadyToRunGenericHelperNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64UnboxingStubNode.cs" />
<Compile Include="Compiler\ExportedMethodsRootProvider.cs" />
<Compile Include="Compiler\UnmanagedEntryPointsRootProvider.cs" />
<Compile Include="Compiler\GenericDictionaryLookup.cs" />
<Compile Include="Compiler\IRootingServiceProvider.cs" />
<Compile Include="Compiler\JitHelper.cs" />
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ internal sealed class ILCompilerRootCommand : RootCommand
new(new[] { "--singlemethodgenericarg" }, "Single method compilation: generic arguments to the method");
public Option<string> MakeReproPath { get; } =
new(new[] { "--make-repro-path" }, "Path where to place a repro package");
public Option<string[]> UnmanagedEntryPointsAssemblies { get; } =
new(new[] { "--generateunmanagedentrypoints" }, Array.Empty<string>, "Generate unmanaged entrypoints for a given assembly");

public OptimizationMode OptimizationMode { get; private set; }
public ParseResult Result;
Expand Down Expand Up @@ -232,6 +234,7 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler")
AddOption(SingleMethodName);
AddOption(SingleMethodGenericArgs);
AddOption(MakeReproPath);
AddOption(UnmanagedEntryPointsAssemblies);

this.SetHandler(context =>
{
Expand Down
15 changes: 11 additions & 4 deletions src/coreclr/tools/aot/ILCompiler/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public int Run()
if (module == typeSystemContext.SystemModule)
systemModuleIsInputModule = true;

compilationRoots.Add(new ExportedMethodsRootProvider(module));
compilationRoots.Add(new UnmanagedEntryPointsRootProvider(module));
}

bool nativeLib = Get(_command.NativeLib);
Expand Down Expand Up @@ -209,8 +209,6 @@ public int Run()
if (entrypointModule == null && (!nativeLib || SplitExeInitialization))
throw new Exception("No entrypoint module");

if (!systemModuleIsInputModule)
compilationRoots.Add(new ExportedMethodsRootProvider((EcmaModule)typeSystemContext.SystemModule));
compilationGroup = new SingleFileCompilationModuleGroup();
}

Expand Down Expand Up @@ -238,6 +236,15 @@ public int Run()
}
}

foreach (var unmanagedEntryPointsAssembly in Get(_command.UnmanagedEntryPointsAssemblies))
{
EcmaModule module = typeSystemContext.GetModuleForSimpleName(unmanagedEntryPointsAssembly);
// Skip adding the system module as a UnmanagedEntryPointsRootProvider if it was already added as an input module
ivanpovazan marked this conversation as resolved.
Show resolved Hide resolved
if (module == typeSystemContext.SystemModule && systemModuleIsInputModule)
continue;
compilationRoots.Add(new UnmanagedEntryPointsRootProvider(module));
}

foreach (var rdXmlFilePath in Get(_command.RdXmlFilePaths))
{
compilationRoots.Add(new RdXmlRootProvider(typeSystemContext, rdXmlFilePath));
Expand Down Expand Up @@ -531,7 +538,7 @@ void RunScanner()
ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, exportsFile);
foreach (var compilationRoot in compilationRoots)
{
if (compilationRoot is ExportedMethodsRootProvider provider)
if (compilationRoot is UnmanagedEntryPointsRootProvider provider)
defFileWriter.AddExportedMethods(provider.ExportedMethods);
}

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/tools/aot/ILCompiler/repro/repro.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<ReproResponseLines Include="--feature:System.Linq.Expressions.CanCompileToIL=false" />
<ReproResponseLines Include="--feature:System.Linq.Expressions.CanEmitObjectArrayDelegate=false" />
<ReproResponseLines Include="--feature:System.Linq.Expressions.CanCreateArbitraryDelegates=false" />
<ReproResponseLines Include="--generateunmanagedentrypoints:System.Private.CoreLib" />
ivanpovazan marked this conversation as resolved.
Show resolved Hide resolved
</ItemGroup>

<WriteLinesToFile File="$(OutputPath)\compile-with-$(LibrariesConfiguration)-libs.rsp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public ILScanResults Trim (ILCompilerOptions options, ILogWriter logWriter)
entrypointModule = module;
}

compilationRoots.Add (new ExportedMethodsRootProvider (module));
compilationRoots.Add (new UnmanagedEntryPointsRootProvider (module));
}

compilationRoots.Add (new MainMethodRootProvider (entrypointModule, CreateInitializerList (typeSystemContext, options), generateLibraryAndModuleInitializers: true));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
project (GenerateUnmanagedEntryPoints)
include_directories(${INC_PLATFORM_DIR})

add_executable (GenerateUnmanagedEntryPointsDriver GenerateUnmanagedEntryPoints.cpp)

if (CLR_CMAKE_TARGET_UNIX)
target_link_libraries (GenerateUnmanagedEntryPointsDriver PRIVATE ${CMAKE_DL_LIBS})
endif()
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#ifdef TARGET_WINDOWS
#include "windows.h"
#else
#include "dlfcn.h"
#endif
#include "stdio.h"
#include "string.h"

#ifndef TARGET_WINDOWS
#define __stdcall
#endif

// typedef for shared lib exported methods
typedef void(__stdcall *fptr)();

#ifdef TARGET_WINDOWS
int __cdecl main()
#else
int main(int argc, char* argv[])
#endif
{
#ifdef TARGET_WINDOWS
HINSTANCE handle = LoadLibrary("GenerateUnmanagedEntryPoints.dll");
ivanpovazan marked this conversation as resolved.
Show resolved Hide resolved
#elif __APPLE__
void *handle = dlopen(strcat(argv[0], ".dylib"), RTLD_LAZY);
#else
void *handle = dlopen(strcat(argv[0], ".so"), RTLD_LAZY);
#endif

if (!handle)
return 1;

#ifdef TARGET_WINDOWS
fptr main_assembly_method = (fptr)GetProcAddress(handle, "SharedLibraryAssemblyMethod");
fptr ref1_assembly_method = (fptr)GetProcAddress(handle, "ReferencedAssembly1Method");
fptr ref2_assembly_method = (fptr)GetProcAddress(handle, "ReferencedAssembly2Method");
#else
fptr main_assembly_method = (fptr)dlsym(handle, "SharedLibraryAssemblyMethod");
ivanpovazan marked this conversation as resolved.
Show resolved Hide resolved
fptr ref1_assembly_method = (fptr)dlsym(handle, "ReferencedAssembly1Method");
fptr ref2_assembly_method = (fptr)dlsym(handle, "ReferencedAssembly2Method");
#endif

// method must be exposed from GenerateUnmanagedEntryPoints assembly
if (!main_assembly_method)
return 2;
main_assembly_method();

// method must be exposed from ReferencedAssembly1 assembly
if (!ref1_assembly_method)
return 3;
ref1_assembly_method();

// method must not be exposed from ReferencedAssembly2 assembly
if (ref2_assembly_method)
return 4;

return 100;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace GenerateUnmanagedEntryPoints
{
public class ClassLibrary
{
[UnmanagedCallersOnly(EntryPoint = "SharedLibraryAssemblyMethod", CallConvs = new Type[] { typeof(CallConvStdcall) })]
static void SharedLibraryAssemblyMethod() => Console.WriteLine($"Hello from {nameof(SharedLibraryAssemblyMethod)}");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<CLRTestKind>BuildAndRun</CLRTestKind>
<NativeLib>Shared</NativeLib>
</PropertyGroup>

<PropertyGroup>
<CLRTestBatchPreCommands><![CDATA[
$(CLRTestBatchPreCommands)
mkdir native 2>nul
copy /y GenerateUnmanagedEntryPointsDriver.exe native\GenerateUnmanagedEntryPoints.exe
]]></CLRTestBatchPreCommands>

<CLRTestBashPreCommands><![CDATA[
$(CLRTestBashPreCommands)
mkdir -p native
cp GenerateUnmanagedEntryPointsDriver native/GenerateUnmanagedEntryPoints
]]></CLRTestBashPreCommands>
</PropertyGroup>

<ItemGroup>
<Compile Include="GenerateUnmanagedEntryPoints.cs" />
<ProjectReference Include="$(MSBuildThisFileDirectory)ReferencedAssembly1\ReferencedAssembly1.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)ReferencedAssembly2\ReferencedAssembly2.csproj" />
</ItemGroup>

<!-- Expose additional unmanaged entry points from ReferencedAssembly1 -->
<ItemGroup>
<UnmanagedEntryPointsAssembly Include="ReferencedAssembly1" />
</ItemGroup>

<ItemGroup>
<CMakeProjectReference Include="CMakeLists.txt" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace ReferencedAssembly1
{
public class ClassLibrary
{
[UnmanagedCallersOnly(EntryPoint = "ReferencedAssembly1Method", CallConvs = new Type[] { typeof(CallConvStdcall) })]
public static void ReferencedAssembly1Method() => Console.WriteLine($"Hello from {nameof(ReferencedAssembly1Method)}");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<Compile Include="ReferencedAssembly1.cs" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace ReferencedAssembly2
{
public class ClassLibrary
{
[UnmanagedCallersOnly(EntryPoint = "ReferencedAssembly2Method", CallConvs = new Type[] { typeof(CallConvStdcall) })]
public static void ReferencedAssembly2Method() => Console.WriteLine($"Hello from {nameof(ReferencedAssembly2Method)}");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<Compile Include="ReferencedAssembly2.cs" />
</ItemGroup>

</Project>