diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props b/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props
index fadd91986c0..76c280d9045 100644
--- a/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props
+++ b/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props
@@ -32,7 +32,8 @@ See the LICENSE file in the project root for more information.
-
+
+
@@ -58,5 +59,6 @@ See the LICENSE file in the project root for more information.
+
diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props b/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props
index 30365d0316e..41b0227a604 100644
--- a/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props
+++ b/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props
@@ -32,7 +32,8 @@ See the LICENSE file in the project root for more information.
-
+
+
@@ -57,6 +58,8 @@ See the LICENSE file in the project root for more information.
+
+
diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.targets b/src/BuildIntegration/Microsoft.NETCore.Native.targets
index 538c373a3ae..5489fbbc572 100644
--- a/src/BuildIntegration/Microsoft.NETCore.Native.targets
+++ b/src/BuildIntegration/Microsoft.NETCore.Native.targets
@@ -39,9 +39,11 @@ See the LICENSE file in the project root for more information.
.exe
- .dll
- .dylib
- .so
+ .dll
+ .dylib
+ .so
+ .lib
+ .a
.html
$(NativeIntermediateOutputPath)$(TargetName)$(NativeObjectExt)
@@ -119,6 +121,7 @@ See the LICENSE file in the project root for more information.
+
@@ -162,10 +165,22 @@ See the LICENSE file in the project root for more information.
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/ILCompiler.Compiler/src/Compiler/NativeLibraryInitializerRootProvider.cs b/src/ILCompiler.Compiler/src/Compiler/NativeLibraryInitializerRootProvider.cs
new file mode 100644
index 00000000000..d7460d45acc
--- /dev/null
+++ b/src/ILCompiler.Compiler/src/Compiler/NativeLibraryInitializerRootProvider.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+
+using Internal.TypeSystem.Ecma;
+using Internal.TypeSystem;
+using Internal.IL.Stubs.StartupCode;
+
+namespace ILCompiler
+{
+ ///
+ /// Provides compilation group for a native library that compiles the initialize method.
+ ///
+ public class NativeLibraryInitializerRootProvider : ICompilationRootProvider
+ {
+ ///
+ /// Symbolic name under which the managed initializer is exported.
+ ///
+ public const string ManagedEntryPointMethodName = "__managed__Startup";
+
+ private EcmaModule _module;
+ private IList _libraryInitializers;
+
+ public NativeLibraryInitializerRootProvider(EcmaModule module, IList libraryInitializers)
+ {
+ _module = module;
+ _libraryInitializers = libraryInitializers;
+ }
+
+ public void AddCompilationRoots(IRootingServiceProvider rootProvider)
+ {
+ TypeDesc owningType = _module.GetGlobalModuleType();
+ NativeLibraryStartupMethod nativeLibStartupCode = new NativeLibraryStartupMethod(owningType, _libraryInitializers);
+ rootProvider.AddCompilationRoot(nativeLibStartupCode, "Startup Code Main Method", ManagedEntryPointMethodName);
+ }
+ }
+}
diff --git a/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.Sorting.cs b/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.Sorting.cs
new file mode 100644
index 00000000000..d8bf6ac0363
--- /dev/null
+++ b/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.Sorting.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+using Internal.TypeSystem;
+
+using Debug = System.Diagnostics.Debug;
+
+namespace Internal.IL.Stubs.StartupCode
+{
+ partial class NativeLibraryStartupMethod
+ {
+ protected override int ClassCode => -304225482;
+
+ protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer)
+ {
+ // Should be a singleton
+ Debug.Assert(this == other);
+ return 0;
+ }
+ }
+}
diff --git a/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.cs b/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.cs
new file mode 100644
index 00000000000..80bd691a019
--- /dev/null
+++ b/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.cs
@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+using Internal.TypeSystem;
+
+namespace Internal.IL.Stubs.StartupCode
+{
+ ///
+ /// Startup code that does initialization, Main invocation
+ /// and shutdown of the runtime.
+ ///
+ public sealed partial class NativeLibraryStartupMethod : ILStubMethod
+ {
+ private TypeDesc _owningType;
+ private MethodSignature _signature;
+ private IList _libraryInitializers;
+
+ public NativeLibraryStartupMethod(TypeDesc owningType, IList libraryInitializers)
+ {
+ _owningType = owningType;
+ _libraryInitializers = libraryInitializers;
+ }
+
+ public override TypeSystemContext Context
+ {
+ get
+ {
+ return _owningType.Context;
+ }
+ }
+
+ public override TypeDesc OwningType
+ {
+ get
+ {
+ return _owningType;
+ }
+ }
+
+ public override string Name
+ {
+ get
+ {
+ return "NativeLibraryStartup";
+ }
+ }
+
+ public override MethodIL EmitIL()
+ {
+ ILEmitter emitter = new ILEmitter();
+ ILCodeStream codeStream = emitter.NewCodeStream();
+
+ // Allow the class library to run explicitly ordered class constructors first thing in start-up.
+ if (_libraryInitializers != null)
+ {
+ foreach (MethodDesc method in _libraryInitializers)
+ {
+ codeStream.Emit(ILOpcode.call, emitter.NewToken(method));
+ }
+ }
+
+ codeStream.Emit(ILOpcode.ret);
+ return emitter.Link(this);
+ }
+
+ public override MethodSignature Signature
+ {
+ get
+ {
+ if (_signature == null)
+ {
+ _signature = new MethodSignature(MethodSignatureFlags.Static, 0,
+ Context.GetWellKnownType(WellKnownType.Void),
+ new TypeDesc[0]);
+ }
+
+ return _signature;
+ }
+ }
+
+ public override bool IsNativeCallable
+ {
+ get
+ {
+ return true;
+ }
+ }
+ }
+}
diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
index 3a1713335ad..73568359fd5 100644
--- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
+++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
@@ -315,6 +315,7 @@
+
@@ -344,6 +345,8 @@
+
+
diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs
index dc2c0f176ca..80a30f5af40 100644
--- a/src/ILCompiler/src/Program.cs
+++ b/src/ILCompiler/src/Program.cs
@@ -39,6 +39,7 @@ internal class Program
private string _ilDump;
private string _systemModuleName = "System.Private.CoreLib";
private bool _multiFile;
+ private bool _nativeLib;
private bool _useSharedGenerics;
private bool _useScanner;
private bool _noScanner;
@@ -126,6 +127,7 @@ private ArgumentSyntax ParseCommandLine(string[] args)
syntax.DefineOption("g", ref _enableDebugInfo, "Emit debugging information");
syntax.DefineOption("cpp", ref _isCppCodegen, "Compile for C++ code-generation");
syntax.DefineOption("wasm", ref _isWasmCodegen, "Compile for WebAssembly code-generation");
+ syntax.DefineOption("nativelib", ref _nativeLib, "Compile as static or shared library");
syntax.DefineOption("dgmllog", ref _dgmlLogFileName, "Save result of dependency analysis as DGML");
syntax.DefineOption("fulllog", ref _generateFullDgmlLog, "Save detailed log of dependency analysis");
syntax.DefineOption("scandgmllog", ref _scanDgmlLogFileName, "Save result of scanner dependency analysis as DGML");
@@ -309,6 +311,12 @@ private int Run(string[] args)
compilationRoots.Add(new RawMainMethodRootProvider(entrypointModule));
}
}
+ else if (_nativeLib)
+ {
+ EcmaModule module = (EcmaModule)typeSystemContext.SystemModule;
+ LibraryInitializers libraryInitializers = new LibraryInitializers(typeSystemContext, _isCppCodegen);
+ compilationRoots.Add(new NativeLibraryInitializerRootProvider(module, libraryInitializers.LibraryInitializerMethods));
+ }
if (_multiFile)
{
@@ -330,7 +338,7 @@ private int Run(string[] args)
}
else
{
- if (entrypointModule == null)
+ if (entrypointModule == null && !_nativeLib)
throw new Exception("No entrypoint module");
// TODO: Wasm fails to compile some of the xported methods due to missing opcodes
diff --git a/src/Native/Bootstrap/CMakeLists.txt b/src/Native/Bootstrap/CMakeLists.txt
index b4c98dac68a..bb7b2280d87 100644
--- a/src/Native/Bootstrap/CMakeLists.txt
+++ b/src/Native/Bootstrap/CMakeLists.txt
@@ -14,3 +14,4 @@ if(NOT CLR_CMAKE_PLATFORM_WASM)
endif(NOT CLR_CMAKE_PLATFORM_WASM)
add_subdirectory(cpp)
+add_subdirectory(dll)
diff --git a/src/Native/Bootstrap/dll/CMakeLists.txt b/src/Native/Bootstrap/dll/CMakeLists.txt
new file mode 100644
index 00000000000..8357d0afb05
--- /dev/null
+++ b/src/Native/Bootstrap/dll/CMakeLists.txt
@@ -0,0 +1,16 @@
+project(bootstrapperdll)
+
+add_definitions(-DCORERT_DLL)
+
+set(SOURCES
+ ../main.cpp
+ ../common.cpp
+)
+
+add_library(bootstrapperdll STATIC ${SOURCES})
+
+# Install the static bootstrapperdll library
+install (TARGETS bootstrapperdll DESTINATION sdk)
+if(WIN32)
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/bootstrapperdll.dir/$/bootstrapperdll.pdb DESTINATION sdk)
+endif()
diff --git a/src/Native/Bootstrap/main.cpp b/src/Native/Bootstrap/main.cpp
index e3c1a2fc9a0..1ba5293d701 100644
--- a/src/Native/Bootstrap/main.cpp
+++ b/src/Native/Bootstrap/main.cpp
@@ -8,7 +8,7 @@
#include "gcenv.structs.h"
#include "gcenv.base.h"
-#include
+#include
#ifndef CPPCODEGEN
@@ -262,6 +262,7 @@ extern "C" void __fail_fast()
extern "C" bool RhInitialize();
extern "C" void RhpEnableConservativeStackReporting();
extern "C" void RhpShutdown();
+extern "C" void RhSetRuntimeInitializationCallback(int (*fPtr)());
#ifndef CPPCODEGEN
@@ -294,13 +295,19 @@ static const pfn c_classlibFunctions[] = {
extern "C" void InitializeModules(void* osModule, void ** modules, int count, void ** pClasslibFunctions, int nClasslibFunctions);
+#ifndef CORERT_DLL
+#define CORERT_ENTRYPOINT __managed__Main
#if defined(_WIN32)
extern "C" int __managed__Main(int argc, wchar_t* argv[]);
-int __cdecl wmain(int argc, wchar_t* argv[])
#else
extern "C" int __managed__Main(int argc, char* argv[]);
-int main(int argc, char* argv[])
#endif
+#else
+#define CORERT_ENTRYPOINT __managed__Startup
+extern "C" void __managed__Startup();
+#endif // !CORERT_DLL
+
+static int InitializeRuntime()
{
if (!RhInitialize())
return -1;
@@ -310,7 +317,7 @@ int main(int argc, char* argv[])
#endif // CPPCODEGEN
#ifndef CPPCODEGEN
- void * osModule = PalGetModuleHandleFromPointer((void*)&__managed__Main);
+ void * osModule = PalGetModuleHandleFromPointer((void*)&CORERT_ENTRYPOINT);
// TODO: pass struct with parameters instead of the large signature of RhRegisterOSModule
if (!RhRegisterOSModule(
osModule,
@@ -328,6 +335,25 @@ int main(int argc, char* argv[])
InitializeModules(nullptr, (void**)RtRHeaderWrapper(), 2, nullptr, 0);
#endif // !CPPCODEGEN
+#ifdef CORERT_DLL
+ // Run startup method immediately for a native library
+ __managed__Startup();
+#endif // CORERT_DLL
+
+ return 0;
+}
+
+#ifndef CORERT_DLL
+#if defined(_WIN32)
+int __cdecl wmain(int argc, wchar_t* argv[])
+#else
+int main(int argc, char* argv[])
+#endif
+{
+ int initval = InitializeRuntime();
+ if (initval != 0)
+ return initval;
+
int retval;
#ifdef CPPCODEGEN
try
@@ -348,3 +374,14 @@ int main(int argc, char* argv[])
return retval;
}
+#endif // !CORERT_DLL
+
+#ifdef CORERT_DLL
+static struct InitializeRuntimePointerHelper
+{
+ InitializeRuntimePointerHelper()
+ {
+ RhSetRuntimeInitializationCallback(&InitializeRuntime);
+ }
+} initializeRuntimePointerHelper;
+#endif // CORERT_DLL
diff --git a/src/Native/Runtime/thread.cpp b/src/Native/Runtime/thread.cpp
index c106963fced..78da88b3bd4 100644
--- a/src/Native/Runtime/thread.cpp
+++ b/src/Native/Runtime/thread.cpp
@@ -33,6 +33,9 @@
EXTERN_C REDHAWK_API void* REDHAWK_CALLCONV RhpHandleAlloc(void* pObject, int type);
EXTERN_C REDHAWK_API void REDHAWK_CALLCONV RhHandleFree(void*);
+static int (*g_RuntimeInitializationCallback)();
+static Thread* g_RuntimeInitializingThread;
+
#ifdef _MSC_VER
extern "C" void _ReadWriteBarrier(void);
#pragma intrinsic(_ReadWriteBarrier)
@@ -1115,10 +1118,22 @@ FORCEINLINE bool Thread::InlineTryFastReversePInvoke(ReversePInvokeFrame * pFram
return true;
}
+EXTERN_C void RhSetRuntimeInitializationCallback(int (*fPtr)())
+{
+ g_RuntimeInitializationCallback = fPtr;
+}
+
void Thread::ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame)
{
if (!IsStateSet(TSF_Attached))
+ {
+ if (g_RuntimeInitializationCallback != NULL && g_RuntimeInitializingThread != this)
+ {
+ EnsureRuntimeInitialized();
+ }
+
ThreadStore::AttachCurrentThread();
+ }
// If the thread is already in cooperative mode, this is a bad transition.
if (IsCurrentThreadInCooperativeMode())
@@ -1149,6 +1164,24 @@ void Thread::ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame)
}
}
+void Thread::EnsureRuntimeInitialized()
+{
+ while (PalInterlockedCompareExchangePointer((void *volatile *)&g_RuntimeInitializingThread, this, NULL) != NULL)
+ {
+ PalSleep(1);
+ }
+
+ if (g_RuntimeInitializationCallback != NULL)
+ {
+ if (g_RuntimeInitializationCallback() != 0)
+ RhFailFast();
+
+ g_RuntimeInitializationCallback = NULL;
+ }
+
+ PalInterlockedExchangePointer((void *volatile *)&g_RuntimeInitializingThread, NULL);
+}
+
FORCEINLINE void Thread::InlineReversePInvokeReturn(ReversePInvokeFrame * pFrame)
{
m_pTransitionFrame = pFrame->m_savedPInvokeTransitionFrame;
diff --git a/src/Native/Runtime/thread.h b/src/Native/Runtime/thread.h
index 63a89c71529..70fd2533167 100644
--- a/src/Native/Runtime/thread.h
+++ b/src/Native/Runtime/thread.h
@@ -142,6 +142,7 @@ class Thread : private ThreadBuffer
void ResetCachedTransitionFrame();
void CrossThreadUnhijack();
void UnhijackWorker();
+ void EnsureRuntimeInitialized();
#ifdef _DEBUG
bool DebugIsSuspended();
#endif
diff --git a/src/Native/Runtime/unix/PalRedhawkUnix.cpp b/src/Native/Runtime/unix/PalRedhawkUnix.cpp
index 0c0c035e1e9..af11b38f0f0 100644
--- a/src/Native/Runtime/unix/PalRedhawkUnix.cpp
+++ b/src/Native/Runtime/unix/PalRedhawkUnix.cpp
@@ -1205,8 +1205,6 @@ REDHAWK_PALEXPORT bool PalGetMaximumStackBounds(_Out_ void** ppStackLowOut, _Out
return true;
}
-extern "C" int main(int argc, char** argv);
-
// retrieves the full path to the specified module, if moduleBase is NULL retreieves the full path to the
// executable module of the current process.
//
@@ -1214,13 +1212,6 @@ extern "C" int main(int argc, char** argv);
//
REDHAWK_PALEXPORT Int32 PalGetModuleFileName(_Out_ const TCHAR** pModuleNameOut, HANDLE moduleBase)
{
- if (moduleBase == NULL)
- {
- // Get an address of the "main" function, which causes the dladdr to return
- // path of the main executable
- moduleBase = (HANDLE)&main;
- }
-
Dl_info dl;
if (dladdr(moduleBase, &dl) == 0)
{