Skip to content

[NativeAOT] Add ability to generate library and exe entry points #81873

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

Merged
merged 7 commits into from
Feb 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -52,8 +52,8 @@ The .NET Foundation licenses this file to you under the MIT license.

<ItemGroup>
<NativeLibrary Condition="'$(IlcMultiModule)' == 'true'" Include="$(SharedLibrary)" />
<NativeLibrary Condition="'$(NativeLib)' == ''" Include="$(IlcSdkPath)libbootstrapper.a" />
<NativeLibrary Condition="'$(NativeLib)' != ''" Include="$(IlcSdkPath)libbootstrapperdll.a" />
<NativeLibrary Condition="'$(NativeLib)' == '' and '$(CustomNativeMain)' != 'true'" Include="$(IlcSdkPath)libbootstrapper.a" />
<NativeLibrary Condition="'$(NativeLib)' != '' or '$(CustomNativeMain)' == 'true'" Include="$(IlcSdkPath)libbootstrapperdll.a" />
<NativeLibrary Include="$(IlcSdkPath)$(FullRuntimeName).a" />
<NativeLibrary Include="$(IlcSdkPath)$(EventPipeName)$(LibFileExt)" />
<NativeLibrary Condition="'$(LinkStandardCPlusPlusLibrary)' != 'true' and '$(StaticICULinking)' != 'true'" Include="$(IlcSdkPath)libstdc++compat.a" />
Expand Down Expand Up @@ -145,8 +145,8 @@ The .NET Foundation licenses this file to you under the MIT license.
<LinkerArg Include="-Wl,-z,relro" Condition="'$(TargetOS)' != 'osx'" />
<!-- binskim warning BA3011 The BIND_NOW flag is missing -->
<LinkerArg Include="-Wl,-z,now" Condition="'$(TargetOS)' != 'osx'" />
<LinkerArg Include="-Wl,-u,$(_SymbolPrefix)NativeAOT_StaticInitialization" Condition="('$(UseLLVMLinker)' == 'true' or '$(TargetOS)' == 'osx' or '$(TargetOS)' == 'freebsd') and '$(NativeLib)' == 'Shared'" />
<LinkerArg Include="-Wl,--require-defined,NativeAOT_StaticInitialization" Condition="'$(UseLLVMLinker)' != 'true' and '$(TargetOS)' == 'linux' and '$(NativeLib)' == 'Shared'" />
<LinkerArg Include="-Wl,-u,$(_SymbolPrefix)NativeAOT_StaticInitialization" Condition="('$(UseLLVMLinker)' == 'true' or '$(TargetOS)' == 'osx' or '$(TargetOS)' == 'freebsd') and ('$(NativeLib)' == 'Shared' or '$(CustomNativeMain)' == 'true')" />
<LinkerArg Include="-Wl,--require-defined,NativeAOT_StaticInitialization" Condition="'$(UseLLVMLinker)' != 'true' and '$(TargetOS)' == 'linux' and ('$(NativeLib)' == 'Shared' or '$(CustomNativeMain)' == 'true')" />
<!-- this workaround can be deleted once the minimum supported glibc version
(runtime's official build machine's glibc version) is at least 2.33
see https://github.com/bminor/glibc/commit/99468ed45f5a58f584bab60364af937eb6f8afda -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The .NET Foundation licenses this file to you under the MIT license.
<FullRuntimeName>Runtime.WorkstationGC</FullRuntimeName>
<FullRuntimeName Condition="'$(ServerGarbageCollection)' == 'true' or '$(ControlFlowGuard)' == 'Guard'">Runtime.ServerGC</FullRuntimeName>
<BootstrapperName>bootstrapper</BootstrapperName>
<BootstrapperName Condition="'$(NativeLib)' != ''">bootstrapperdll</BootstrapperName>
<BootstrapperName Condition="'$(NativeLib)' != '' or '$(CustomNativeMain)' == 'true'">bootstrapperdll</BootstrapperName>
<EntryPointSymbol Condition="'$(EntryPointSymbol)' == ''">wmainCRTStartup</EntryPointSymbol>
<LinkerSubsystem Condition="'$(OutputType)' == 'WinExe' and '$(LinkerSubsystem)' == ''">WINDOWS</LinkerSubsystem>
<LinkerSubsystem Condition="'$(OutputType)' == 'Exe' and '$(LinkerSubsystem)' == ''">CONSOLE</LinkerSubsystem>
Expand Down Expand Up @@ -84,7 +84,7 @@ The .NET Foundation licenses this file to you under the MIT license.
<LinkerArg Include="/INCREMENTAL:NO" />
<LinkerArg Condition="'$(LinkerSubsystem)' != ''" Include="/SUBSYSTEM:$(LinkerSubsystem)" />
<LinkerArg Condition="'$(OutputType)' == 'WinExe' or '$(OutputType)' == 'Exe'" Include="/ENTRY:$(EntryPointSymbol) /NOEXP /NOIMPLIB" />
<LinkerArg Condition="'$(NativeLib)' == 'Shared'" Include="/INCLUDE:NativeAOT_StaticInitialization" />
<LinkerArg Condition="'$(NativeLib)' == 'Shared' or '$(CustomNativeMain)' == 'true'" Include="/INCLUDE:NativeAOT_StaticInitialization" />
<LinkerArg Include="/NATVIS:&quot;$(MSBuildThisFileDirectory)NativeAOT.natvis&quot;" />
<LinkerArg Condition="'$(ControlFlowGuard)' == 'Guard'" Include="/guard:cf" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ The .NET Foundation licenses this file to you under the MIT license.
<IlcArg Include="@(RdXmlFile->'--rdxml:%(FullPath)')" />
<IlcArg Include="@(TrimmerRootDescriptor->'--descriptor:%(FullPath)')" />
<IlcArg Condition="'$(NativeLib)' != ''" Include="--nativelib" />
<IlcArg Condition="'$(CustomNativeMain)' == 'true'" Include="--splitinit" />
<IlcArg Condition="$(ExportsFile) != ''" Include="--exportsfile:$(ExportsFile)" />
<IlcArg Include="@(AutoInitializedAssemblies->'--initassembly:%(Identity)')" />
<IlcArg Include="@(RuntimeHostConfigurationOption->'--appcontextswitch:%(Identity)=%(Value)')" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ public class MainMethodRootProvider : ICompilationRootProvider

private EcmaModule _module;
private IReadOnlyCollection<MethodDesc> _libraryInitializers;
private bool _generateLibraryAndModuleInitializers;

public MainMethodRootProvider(EcmaModule module, IReadOnlyCollection<MethodDesc> libraryInitializers)
public MainMethodRootProvider(EcmaModule module, IReadOnlyCollection<MethodDesc> libraryInitializers, bool generateLibraryAndModuleInitializers)
{
_module = module;
_libraryInitializers = libraryInitializers;
_generateLibraryAndModuleInitializers = generateLibraryAndModuleInitializers;
}

public void AddCompilationRoots(IRootingServiceProvider rootProvider)
Expand All @@ -36,7 +38,7 @@ public void AddCompilationRoots(IRootingServiceProvider rootProvider)
throw new Exception("No managed entrypoint defined for executable module");

TypeDesc owningType = _module.GetGlobalModuleType();
var startupCodeMain = new StartupCodeMainMethod(owningType, mainMethod, _libraryInitializers);
var startupCodeMain = new StartupCodeMainMethod(owningType, mainMethod, _libraryInitializers, _generateLibraryAndModuleInitializers);

rootProvider.AddCompilationRoot(startupCodeMain, "Startup Code Main Method", ManagedEntryPointMethodName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ public sealed partial class StartupCodeMainMethod : ILStubMethod
private MainMethodWrapper _mainMethod;
private MethodSignature _signature;
private IReadOnlyCollection<MethodDesc> _libraryInitializers;
private bool _generateLibraryAndModuleInitializers;

public StartupCodeMainMethod(TypeDesc owningType, MethodDesc mainMethod, IReadOnlyCollection<MethodDesc> libraryInitializers)
public StartupCodeMainMethod(TypeDesc owningType, MethodDesc mainMethod, IReadOnlyCollection<MethodDesc> libraryInitializers, bool generateLibraryAndModuleInitializers)
{
_owningType = owningType;
_mainMethod = new MainMethodWrapper(owningType, mainMethod);
_libraryInitializers = libraryInitializers;
_generateLibraryAndModuleInitializers = generateLibraryAndModuleInitializers;
}

public override TypeSystemContext Context
Expand Down Expand Up @@ -68,7 +70,7 @@ public override MethodIL EmitIL()
codeStream.MarkDebuggerStepThroughPoint();

// Allow the class library to run explicitly ordered class constructors first thing in start-up.
if (_libraryInitializers != null)
if (_generateLibraryAndModuleInitializers && _libraryInitializers != null)
{
foreach (MethodDesc method in _libraryInitializers)
{
Expand Down Expand Up @@ -118,7 +120,7 @@ public override MethodIL EmitIL()

// Run module initializers
MethodDesc runModuleInitializers = startup?.GetMethod("RunModuleInitializers", null);
if (runModuleInitializers != null)
if (_generateLibraryAndModuleInitializers && runModuleInitializers != null)
{
codeStream.Emit(ILOpcode.call, emitter.NewToken(runModuleInitializers));
}
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 @@ -34,6 +34,8 @@ internal sealed class ILCompilerRootCommand : RootCommand
new(new[] { "--gdwarf-5" }, "Generate source-level debug information with dwarf version 5");
public Option<bool> NativeLib { get; } =
new(new[] { "--nativelib" }, "Compile as static or shared library");
public Option<bool> SplitExeInitialization { get; } =
new(new[] { "--splitinit" }, "Split initialization of an executable between the library entrypoint and a main entrypoint");
public Option<string> ExportsFile { get; } =
new(new[] { "--exportsfile" }, "File to write exported method definitions");
public Option<string> DgmlLogFileName { get; } =
Expand Down Expand Up @@ -174,6 +176,7 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler")
AddOption(EnableDebugInfo);
AddOption(UseDwarf5);
AddOption(NativeLib);
AddOption(SplitExeInitialization);
AddOption(ExportsFile);
AddOption(DgmlLogFileName);
AddOption(GenerateFullDgmlLog);
Expand Down
13 changes: 11 additions & 2 deletions src/coreclr/tools/aot/ILCompiler/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ public int Run()
}

bool nativeLib = Get(_command.NativeLib);
bool SplitExeInitialization = Get(_command.SplitExeInitialization);
if (multiFile)
{
List<EcmaModule> inputModules = new List<EcmaModule>();
Expand All @@ -205,7 +206,7 @@ public int Run()
}
else
{
if (entrypointModule == null && !nativeLib)
if (entrypointModule == null && (!nativeLib || SplitExeInitialization))
throw new Exception("No entrypoint module");

if (!systemModuleIsInputModule)
Expand All @@ -221,12 +222,20 @@ public int Run()
compilationRoots.Add(new NativeLibraryInitializerRootProvider(typeSystemContext.GeneratedAssembly, CreateInitializerList(typeSystemContext)));
compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions));
compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport));
if (SplitExeInitialization)
{
compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext), generateLibraryAndModuleInitializers: false));
}
}
else if (entrypointModule != null)
{
compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext)));
compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext), generateLibraryAndModuleInitializers: !SplitExeInitialization));
compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions));
compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport));
if (SplitExeInitialization)
{
compilationRoots.Add(new NativeLibraryInitializerRootProvider(typeSystemContext.GeneratedAssembly, CreateInitializerList(typeSystemContext)));
}
}

foreach (var rdXmlFilePath in Get(_command.RdXmlFilePaths))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public ILScanResults Trim (ILCompilerOptions options, ILogWriter logWriter)
compilationRoots.Add (new ExportedMethodsRootProvider (module));
}

compilationRoots.Add (new MainMethodRootProvider (entrypointModule, CreateInitializerList (typeSystemContext, options)));
compilationRoots.Add (new MainMethodRootProvider (entrypointModule, CreateInitializerList (typeSystemContext, options), generateLibraryAndModuleInitializers: true));

ILProvider ilProvider = new NativeAotILProvider ();

Expand Down
7 changes: 7 additions & 0 deletions src/tests/nativeaot/CustomMain/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
project (CustomMainNative)
include_directories(${INC_PLATFORM_DIR})

add_library (CustomMainNative STATIC CustomMainNative.cpp)

# add the install targets
install (TARGETS CustomMainNative DESTINATION bin)
40 changes: 40 additions & 0 deletions src/tests/nativeaot/CustomMain/CustomMain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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;

class Program
{
// Each of the module initializer, class constructor, and IncrementExitCode
// should be executed exactly once, causing this to each 100 by program exit.
static int s_exitCode;

[ModuleInitializer]
internal static void InitializeModule()
{
s_exitCode += 8;
}

static Program()
{
s_exitCode += 31;
// A side-effecting operation to prevent this cctor from being pre-inited at compile time.
Console.WriteLine("hello from static constructor");
}

[UnmanagedCallersOnly(EntryPoint = "IncrementExitCode", CallConvs = new Type[] { typeof(CallConvCdecl) })]
static void IncrementExitCode(int amount)
{
s_exitCode += amount;
}

int ExitCode;

static int Main(string[] args)
{
Console.WriteLine("hello from managed main");
return s_exitCode;
}
}
19 changes: 19 additions & 0 deletions src/tests/nativeaot/CustomMain/CustomMain.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<CustomNativeMain>true</CustomNativeMain>
<StaticLibraryPrefix Condition="'$(TargetOS)' != 'windows'">lib</StaticLibraryPrefix>
</PropertyGroup>

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

<ItemGroup>
<CMakeProjectReference Include="CMakeLists.txt" />
</ItemGroup>

<ItemGroup>
<NativeLibrary Include="$(OutputPath)$(StaticLibraryPrefix)CustomMainNative$(LibFileExt)" />
</ItemGroup>
</Project>
24 changes: 24 additions & 0 deletions src/tests/nativeaot/CustomMain/CustomMainNative.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include <stdio.h>
#include <stdint.h>

#if defined(_WIN32)
extern "C" int __managed__Main(int argc, wchar_t* argv[]);
#else
extern "C" int __managed__Main(int argc, char* argv[]);
#endif

extern "C" void IncrementExitCode(int32_t amount);

#if defined(_WIN32)
int __cdecl wmain(int argc, wchar_t* argv[])
#else
int main(int argc, char* argv[])
#endif
{
puts("hello from native main");
IncrementExitCode(61);
return __managed__Main(argc, argv);
}