diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets
index 70367002ba9593..c28a87aa84eac6 100644
--- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets
+++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets
@@ -52,8 +52,8 @@ The .NET Foundation licenses this file to you under the MIT license.
-
-
+
+
@@ -145,8 +145,8 @@ The .NET Foundation licenses this file to you under the MIT license.
-
-
+
+
diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets
index 5c115640adc8d0..293c24f9fab00a 100644
--- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets
+++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets
@@ -21,7 +21,7 @@ The .NET Foundation licenses this file to you under the MIT license.
Runtime.WorkstationGC
Runtime.ServerGC
bootstrapper
- bootstrapperdll
+ bootstrapperdll
wmainCRTStartup
WINDOWS
CONSOLE
@@ -84,7 +84,7 @@ The .NET Foundation licenses this file to you under the MIT license.
-
+
diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
index 5caefcd10b26f1..64d2468572a65c 100644
--- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
+++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
@@ -208,6 +208,7 @@ The .NET Foundation licenses this file to you under the MIT license.
+
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MainMethodRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MainMethodRootProvider.cs
index 2d77ba865e6a25..9a5460ff0cbbdf 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MainMethodRootProvider.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MainMethodRootProvider.cs
@@ -22,11 +22,13 @@ public class MainMethodRootProvider : ICompilationRootProvider
private EcmaModule _module;
private IReadOnlyCollection _libraryInitializers;
+ private bool _generateLibraryAndModuleInitializers;
- public MainMethodRootProvider(EcmaModule module, IReadOnlyCollection libraryInitializers)
+ public MainMethodRootProvider(EcmaModule module, IReadOnlyCollection libraryInitializers, bool generateLibraryAndModuleInitializers)
{
_module = module;
_libraryInitializers = libraryInitializers;
+ _generateLibraryAndModuleInitializers = generateLibraryAndModuleInitializers;
}
public void AddCompilationRoots(IRootingServiceProvider rootProvider)
@@ -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);
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs
index 7d197f85e08d52..2b88c59e1c0cc3 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs
@@ -19,12 +19,14 @@ public sealed partial class StartupCodeMainMethod : ILStubMethod
private MainMethodWrapper _mainMethod;
private MethodSignature _signature;
private IReadOnlyCollection _libraryInitializers;
+ private bool _generateLibraryAndModuleInitializers;
- public StartupCodeMainMethod(TypeDesc owningType, MethodDesc mainMethod, IReadOnlyCollection libraryInitializers)
+ public StartupCodeMainMethod(TypeDesc owningType, MethodDesc mainMethod, IReadOnlyCollection libraryInitializers, bool generateLibraryAndModuleInitializers)
{
_owningType = owningType;
_mainMethod = new MainMethodWrapper(owningType, mainMethod);
_libraryInitializers = libraryInitializers;
+ _generateLibraryAndModuleInitializers = generateLibraryAndModuleInitializers;
}
public override TypeSystemContext Context
@@ -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)
{
@@ -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));
}
diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs
index 235b7443e55482..9517d47e50a0f6 100644
--- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs
+++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs
@@ -34,6 +34,8 @@ internal sealed class ILCompilerRootCommand : RootCommand
new(new[] { "--gdwarf-5" }, "Generate source-level debug information with dwarf version 5");
public Option NativeLib { get; } =
new(new[] { "--nativelib" }, "Compile as static or shared library");
+ public Option SplitExeInitialization { get; } =
+ new(new[] { "--splitinit" }, "Split initialization of an executable between the library entrypoint and a main entrypoint");
public Option ExportsFile { get; } =
new(new[] { "--exportsfile" }, "File to write exported method definitions");
public Option DgmlLogFileName { get; } =
@@ -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);
diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs
index b82350c51e54d8..893005a1f4f3d1 100644
--- a/src/coreclr/tools/aot/ILCompiler/Program.cs
+++ b/src/coreclr/tools/aot/ILCompiler/Program.cs
@@ -185,6 +185,7 @@ public int Run()
}
bool nativeLib = Get(_command.NativeLib);
+ bool SplitExeInitialization = Get(_command.SplitExeInitialization);
if (multiFile)
{
List inputModules = new List();
@@ -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)
@@ -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))
diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs
index f5100d6acc1324..eba47f24bb8234 100644
--- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs
+++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs
@@ -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 ();
diff --git a/src/tests/nativeaot/CustomMain/CMakeLists.txt b/src/tests/nativeaot/CustomMain/CMakeLists.txt
new file mode 100644
index 00000000000000..a9c911110e9fb7
--- /dev/null
+++ b/src/tests/nativeaot/CustomMain/CMakeLists.txt
@@ -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)
diff --git a/src/tests/nativeaot/CustomMain/CustomMain.cs b/src/tests/nativeaot/CustomMain/CustomMain.cs
new file mode 100644
index 00000000000000..914809961d778d
--- /dev/null
+++ b/src/tests/nativeaot/CustomMain/CustomMain.cs
@@ -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;
+ }
+}
diff --git a/src/tests/nativeaot/CustomMain/CustomMain.csproj b/src/tests/nativeaot/CustomMain/CustomMain.csproj
new file mode 100644
index 00000000000000..f3787228534828
--- /dev/null
+++ b/src/tests/nativeaot/CustomMain/CustomMain.csproj
@@ -0,0 +1,19 @@
+
+
+ Exe
+ true
+ lib
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tests/nativeaot/CustomMain/CustomMainNative.cpp b/src/tests/nativeaot/CustomMain/CustomMainNative.cpp
new file mode 100644
index 00000000000000..590051f6c5e7f2
--- /dev/null
+++ b/src/tests/nativeaot/CustomMain/CustomMainNative.cpp
@@ -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
+#include
+
+#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);
+}