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); +}