summaryrefslogtreecommitdiff
path: root/src/coreclr/hosts
diff options
context:
space:
mode:
Diffstat (limited to 'src/coreclr/hosts')
-rw-r--r--src/coreclr/hosts/.gitmirror1
-rw-r--r--src/coreclr/hosts/CMakeLists.txt13
-rw-r--r--src/coreclr/hosts/coreconsole/.gitmirror1
-rw-r--r--src/coreclr/hosts/coreconsole/CMakeLists.txt33
-rw-r--r--src/coreclr/hosts/coreconsole/CoreConsole.nativeproj32
-rw-r--r--src/coreclr/hosts/coreconsole/coreconsole.cpp669
-rw-r--r--src/coreclr/hosts/coreconsole/logger.cpp112
-rw-r--r--src/coreclr/hosts/coreconsole/logger.h54
-rw-r--r--src/coreclr/hosts/coreconsole/native.rc8
-rw-r--r--src/coreclr/hosts/corerun/.gitmirror1
-rw-r--r--src/coreclr/hosts/corerun/CMakeLists.txt38
-rw-r--r--src/coreclr/hosts/corerun/coreRun.nativeproj45
-rw-r--r--src/coreclr/hosts/corerun/corerun.cpp691
-rw-r--r--src/coreclr/hosts/corerun/logger.cpp269
-rw-r--r--src/coreclr/hosts/corerun/logger.h56
-rw-r--r--src/coreclr/hosts/corerun/native.rc8
-rw-r--r--src/coreclr/hosts/corerun/test.txt1
-rw-r--r--src/coreclr/hosts/dirs.proj19
-rw-r--r--src/coreclr/hosts/inc/.gitmirrorall1
-rw-r--r--src/coreclr/hosts/inc/coreclrhost.h50
-rw-r--r--src/coreclr/hosts/osxbundlerun/.gitmirrorall1
-rw-r--r--src/coreclr/hosts/osxbundlerun/CMakeLists.txt24
-rw-r--r--src/coreclr/hosts/osxbundlerun/osxbundlerun.cpp90
-rw-r--r--src/coreclr/hosts/unixcoreconsole/.gitmirrorall1
-rw-r--r--src/coreclr/hosts/unixcoreconsole/CMakeLists.txt33
-rw-r--r--src/coreclr/hosts/unixcoreconsole/coreconsole.cpp162
-rw-r--r--src/coreclr/hosts/unixcorerun/.gitmirrorall1
-rw-r--r--src/coreclr/hosts/unixcorerun/CMakeLists.txt33
-rw-r--r--src/coreclr/hosts/unixcorerun/corerun.cpp162
-rw-r--r--src/coreclr/hosts/unixcoreruncommon/.gitmirror1
-rw-r--r--src/coreclr/hosts/unixcoreruncommon/CMakeLists.txt12
-rw-r--r--src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp454
-rw-r--r--src/coreclr/hosts/unixcoreruncommon/coreruncommon.h56
33 files changed, 3132 insertions, 0 deletions
diff --git a/src/coreclr/hosts/.gitmirror b/src/coreclr/hosts/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/coreclr/hosts/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/coreclr/hosts/CMakeLists.txt b/src/coreclr/hosts/CMakeLists.txt
new file mode 100644
index 0000000000..bb425b908a
--- /dev/null
+++ b/src/coreclr/hosts/CMakeLists.txt
@@ -0,0 +1,13 @@
+include_directories(inc)
+
+if(WIN32)
+ add_subdirectory(corerun)
+ add_subdirectory(coreconsole)
+else(WIN32)
+ add_subdirectory(unixcoreruncommon)
+ add_subdirectory(unixcorerun)
+ add_subdirectory(unixcoreconsole)
+ if(CLR_CMAKE_PLATFORM_DARWIN)
+ add_subdirectory(osxbundlerun)
+ endif(CLR_CMAKE_PLATFORM_DARWIN)
+endif(WIN32)
diff --git a/src/coreclr/hosts/coreconsole/.gitmirror b/src/coreclr/hosts/coreconsole/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/coreclr/hosts/coreconsole/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/coreclr/hosts/coreconsole/CMakeLists.txt b/src/coreclr/hosts/coreconsole/CMakeLists.txt
new file mode 100644
index 0000000000..a8eae3209b
--- /dev/null
+++ b/src/coreclr/hosts/coreconsole/CMakeLists.txt
@@ -0,0 +1,33 @@
+project(CoreConsole)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CoreConsole_SOURCES coreconsole.cpp logger.cpp)
+set(CoreConsole_RESOURCES native.rc)
+
+add_definitions(-DFX_VER_INTERNALNAME_STR=CoreConsole.exe)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ # This does not compile on Linux yet
+ if(CAN_BE_COMPILED_ON_LINUX)
+ _add_executable(CoreConsole
+ ${CoreConsole_SOURCES}
+ ${CoreConsole_RESOURCES}
+ )
+ endif(CAN_BE_COMPILED_ON_LINUX)
+
+else()
+ _add_executable(CoreConsole
+ ${CoreConsole_SOURCES}
+ ${CoreConsole_RESOURCES}
+ )
+
+ target_link_libraries(CoreConsole
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_VCRT_LIB}
+ )
+
+ # Can't compile on linux yet so only add for windows
+ install_clr(CoreConsole)
+
+endif(CLR_CMAKE_PLATFORM_UNIX) \ No newline at end of file
diff --git a/src/coreclr/hosts/coreconsole/CoreConsole.nativeproj b/src/coreclr/hosts/coreconsole/CoreConsole.nativeproj
new file mode 100644
index 0000000000..bdb190fbbb
--- /dev/null
+++ b/src/coreclr/hosts/coreconsole/CoreConsole.nativeproj
@@ -0,0 +1,32 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>CoreConsole</OutputName>
+ <TargetType>PROGRAM</TargetType>
+ <LinkSubsystem>console</LinkSubsystem>
+ <EntryPoint>wmain</EntryPoint>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE</ClAdditionalOptions>
+ <IsTestNetHost>true</IsTestNetHost>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <TargetLib Include="$(CoreSystemCrt)" />
+ <TargetLib Condition="'$(BuildForWindows7)'=='true'" Include="$(SdkLibPath)\mincore_fw.lib" />
+ <TargetLib Condition="'$(BuildForWindows7)'!='true'" Include="$(SdkLibPath)\mincore.lib" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <RCResourceFile Include="native.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <CppCompile Include="coreconsole.cpp" />
+ <CppCompile Include="logger.cpp" />
+ </ItemGroup>
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/coreclr/hosts/coreconsole/coreconsole.cpp b/src/coreclr/hosts/coreconsole/coreconsole.cpp
new file mode 100644
index 0000000000..1533dff88e
--- /dev/null
+++ b/src/coreclr/hosts/coreconsole/coreconsole.cpp
@@ -0,0 +1,669 @@
+// 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.
+
+//
+// A simple CoreCLR host that runs a managed binary with the same name as this executable but with *.dll extension
+// The dll binary must contain main entry point.
+//
+
+#include "windows.h"
+#include <stdio.h>
+#include "mscoree.h"
+#include <Logger.h>
+#include "palclr.h"
+
+// The name of the CoreCLR native runtime DLL.
+static const wchar_t *coreCLRDll = W("CoreCLR.dll");
+
+// Dynamically expanding string buffer to hold TPA list
+class StringBuffer {
+ static const int m_defaultSize = 4096;
+ wchar_t* m_buffer;
+ size_t m_capacity;
+ size_t m_length;
+
+ StringBuffer(const StringBuffer&);
+ StringBuffer& operator =(const StringBuffer&);
+
+public:
+ StringBuffer() : m_capacity(0), m_buffer(nullptr), m_length(0) {
+ }
+
+ ~StringBuffer() {
+ delete[] m_buffer;
+ }
+
+ const wchar_t* CStr() const {
+ return m_buffer;
+ }
+
+ void Append(const wchar_t* str, size_t strLen) {
+ if (!m_buffer) {
+ m_buffer = new wchar_t[m_defaultSize];
+ m_capacity = m_defaultSize;
+ }
+ if (m_length + strLen + 1 > m_capacity) {
+ size_t newCapacity = m_capacity * 2;
+ wchar_t* newBuffer = new wchar_t[newCapacity];
+ wcsncpy_s(newBuffer, newCapacity, m_buffer, m_length);
+ delete[] m_buffer;
+ m_buffer = newBuffer;
+ m_capacity = newCapacity;
+ }
+ wcsncpy_s(m_buffer + m_length, m_capacity - m_length, str, strLen);
+ m_length += strLen;
+ }
+};
+
+// Encapsulates the environment that CoreCLR will run in, including the TPALIST
+class HostEnvironment
+{
+ // The path to this module
+ wchar_t m_hostPath[MAX_LONGPATH];
+
+ // The path to the directory containing this module
+ wchar_t m_hostDirectoryPath[MAX_LONGPATH];
+
+ // The name of this module, without the path
+ wchar_t *m_hostExeName;
+
+ // The list of paths to the assemblies that will be trusted by CoreCLR
+ StringBuffer m_tpaList;
+
+ ICLRRuntimeHost2* m_CLRRuntimeHost;
+
+ HMODULE m_coreCLRModule;
+
+ Logger *m_log;
+
+
+ // Attempts to load CoreCLR.dll from the given directory.
+ // On success pins the dll, sets m_coreCLRDirectoryPath and returns the HMODULE.
+ // On failure returns nullptr.
+ HMODULE TryLoadCoreCLR(const wchar_t* directoryPath) {
+
+ wchar_t coreCLRPath[MAX_LONGPATH];
+ wcscpy_s(coreCLRPath, directoryPath);
+ wcscat_s(coreCLRPath, coreCLRDll);
+
+ *m_log << W("Attempting to load: ") << coreCLRPath << Logger::endl;
+
+ HMODULE result = ::LoadLibraryExW(coreCLRPath, NULL, 0);
+ if (!result) {
+ *m_log << W("Failed to load: ") << coreCLRPath << Logger::endl;
+ *m_log << W("Error code: ") << GetLastError() << Logger::endl;
+ return nullptr;
+ }
+
+ // Pin the module - CoreCLR.dll does not support being unloaded.
+ HMODULE dummy_coreCLRModule;
+ if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, coreCLRPath, &dummy_coreCLRModule)) {
+ *m_log << W("Failed to pin: ") << coreCLRPath << Logger::endl;
+ return nullptr;
+ }
+
+ wchar_t coreCLRLoadedPath[MAX_LONGPATH];
+ ::GetModuleFileNameW(result, coreCLRLoadedPath, MAX_LONGPATH);
+
+ *m_log << W("Loaded: ") << coreCLRLoadedPath << Logger::endl;
+
+ return result;
+ }
+
+public:
+ // The path to the directory that CoreCLR is in
+ wchar_t m_coreCLRDirectoryPath[MAX_LONGPATH];
+
+ HostEnvironment(Logger *logger)
+ : m_log(logger), m_CLRRuntimeHost(nullptr) {
+
+ // Discover the path to this exe's module. All other files are expected to be in the same directory.
+ DWORD thisModuleLength = ::GetModuleFileNameW(::GetModuleHandleW(nullptr), m_hostPath, MAX_LONGPATH);
+
+ // Search for the last backslash in the host path.
+ int lastBackslashIndex;
+ for (lastBackslashIndex = thisModuleLength-1; lastBackslashIndex >= 0; lastBackslashIndex--) {
+ if (m_hostPath[lastBackslashIndex] == W('\\')) {
+ break;
+ }
+ }
+
+ // Copy the directory path
+ ::wcsncpy_s(m_hostDirectoryPath, m_hostPath, lastBackslashIndex + 1);
+
+ // Save the exe name
+ m_hostExeName = m_hostPath + lastBackslashIndex + 1;
+
+ *m_log << W("Host directory: ") << m_hostDirectoryPath << Logger::endl;
+
+ // Check for %CORE_ROOT% and try to load CoreCLR.dll from it if it is set
+ wchar_t coreRoot[MAX_LONGPATH];
+ size_t outSize;
+ m_coreCLRModule = NULL; // Initialize this here since we don't call TryLoadCoreCLR if CORE_ROOT is unset.
+ if (_wgetenv_s(&outSize, coreRoot, MAX_LONGPATH, W("CORE_ROOT")) == 0 && outSize > 0)
+ {
+ wcscat_s(coreRoot, MAX_LONGPATH, W("\\"));
+ m_coreCLRModule = TryLoadCoreCLR(coreRoot);
+ }
+ else
+ {
+ *m_log << W("CORE_ROOT not set; skipping") << Logger::endl;
+ *m_log << W("You can set the environment variable CORE_ROOT to point to the path") << Logger::endl;
+ *m_log << W("where CoreCLR.dll lives to help this executable find it.") << Logger::endl;
+ }
+
+ // Try to load CoreCLR from the directory that this exexutable is in
+ if (!m_coreCLRModule) {
+ m_coreCLRModule = TryLoadCoreCLR(m_hostDirectoryPath);
+ }
+
+ if (m_coreCLRModule) {
+
+ // Save the directory that CoreCLR was found in
+ DWORD modulePathLength = ::GetModuleFileNameW(m_coreCLRModule, m_coreCLRDirectoryPath, MAX_LONGPATH);
+
+ // Search for the last backslash and terminate it there to keep just the directory path with trailing slash
+ for (lastBackslashIndex = modulePathLength-1; lastBackslashIndex >= 0; lastBackslashIndex--) {
+ if (m_coreCLRDirectoryPath[lastBackslashIndex] == W('\\')) {
+ m_coreCLRDirectoryPath[lastBackslashIndex + 1] = W('\0');
+ break;
+ }
+ }
+
+ } else {
+ *m_log << W("Unable to load ") << coreCLRDll << Logger::endl;
+ }
+ }
+
+ ~HostEnvironment() {
+ if(m_coreCLRModule) {
+ // Free the module. This is done for completeness, but in fact CoreCLR.dll
+ // was pinned earlier so this call won't actually free it. The pinning is
+ // done because CoreCLR does not support unloading.
+ ::FreeLibrary(m_coreCLRModule);
+ }
+ }
+
+ bool TPAListContainsFile(_In_z_ wchar_t* fileNameWithoutExtension, _In_reads_(countExtensions) wchar_t** rgTPAExtensions, int countExtensions)
+ {
+ if (!m_tpaList.CStr()) return false;
+
+ for (int iExtension = 0; iExtension < countExtensions; iExtension++)
+ {
+ wchar_t fileName[MAX_LONGPATH];
+ wcscpy_s(fileName, MAX_LONGPATH, W("\\")); // So that we don't match other files that end with the current file name
+ wcscat_s(fileName, MAX_LONGPATH, fileNameWithoutExtension);
+ wcscat_s(fileName, MAX_LONGPATH, rgTPAExtensions[iExtension] + 1);
+ wcscat_s(fileName, MAX_LONGPATH, W(";")); // So that we don't match other files that begin with the current file name
+
+ if (wcsstr(m_tpaList.CStr(), fileName))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void RemoveExtensionAndNi(_In_z_ wchar_t* fileName)
+ {
+ // Remove extension, if it exists
+ wchar_t* extension = wcsrchr(fileName, W('.'));
+ if (extension != NULL)
+ {
+ extension[0] = W('\0');
+
+ // Check for .ni
+ size_t len = wcslen(fileName);
+ if (len > 3 &&
+ fileName[len - 1] == W('i') &&
+ fileName[len - 2] == W('n') &&
+ fileName[len - 3] == W('.') )
+ {
+ fileName[len - 3] = W('\0');
+ }
+ }
+ }
+
+ void AddFilesFromDirectoryToTPAList(_In_z_ wchar_t* targetPath, _In_reads_(countExtensions) wchar_t** rgTPAExtensions, int countExtensions)
+ {
+ *m_log << W("Adding assemblies from ") << targetPath << W(" to the TPA list") << Logger::endl;
+ wchar_t assemblyPath[MAX_LONGPATH];
+
+ for (int iExtension = 0; iExtension < countExtensions; iExtension++)
+ {
+ wcscpy_s(assemblyPath, MAX_LONGPATH, targetPath);
+
+ const size_t dirLength = wcslen(targetPath);
+ wchar_t* const fileNameBuffer = assemblyPath + dirLength;
+ const size_t fileNameBufferSize = MAX_LONGPATH - dirLength;
+
+ wcscat_s(assemblyPath, rgTPAExtensions[iExtension]);
+ WIN32_FIND_DATA data;
+ HANDLE findHandle = FindFirstFile(assemblyPath, &data);
+
+ if (findHandle != INVALID_HANDLE_VALUE) {
+ do {
+ if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ // It seems that CoreCLR doesn't always use the first instance of an assembly on the TPA list (ni's may be preferred
+ // over il, even if they appear later). So, only include the first instance of a simple assembly name to allow
+ // users the opportunity to override Framework assemblies by placing dlls in %CORE_LIBRARIES%
+
+ // ToLower for case-insensitive comparisons
+ wchar_t* fileNameChar = data.cFileName;
+ while (*fileNameChar)
+ {
+ *fileNameChar = towlower(*fileNameChar);
+ fileNameChar++;
+ }
+
+ // Remove extension
+ wchar_t fileNameWithoutExtension[MAX_LONGPATH];
+ wcscpy_s(fileNameWithoutExtension, MAX_LONGPATH, data.cFileName);
+
+ RemoveExtensionAndNi(fileNameWithoutExtension);
+
+ // Add to the list if not already on it
+ if (!TPAListContainsFile(fileNameWithoutExtension, rgTPAExtensions, countExtensions))
+ {
+ const size_t fileLength = wcslen(data.cFileName);
+ const size_t assemblyPathLength = dirLength + fileLength;
+ wcsncpy_s(fileNameBuffer, fileNameBufferSize, data.cFileName, fileLength);
+ m_tpaList.Append(assemblyPath, assemblyPathLength);
+ m_tpaList.Append(W(";"), 1);
+ }
+ else
+ {
+ *m_log << W("Not adding ") << targetPath << data.cFileName << W(" to the TPA list because another file with the same name is already present on the list") << Logger::endl;
+ }
+ }
+ } while (0 != FindNextFile(findHandle, &data));
+
+ FindClose(findHandle);
+ }
+ }
+ }
+
+ // Returns the semicolon-separated list of paths to runtime dlls that are considered trusted.
+ // On first call, scans the coreclr directory for dlls and adds them all to the list.
+ const wchar_t * GetTpaList() {
+ if (!m_tpaList.CStr()) {
+ wchar_t *rgTPAExtensions[] = {
+ W("*.ni.dll"), // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+ W("*.dll"),
+ W("*.ni.exe"),
+ W("*.exe"),
+ };
+
+ // Add files from %CORE_LIBRARIES% if specified
+ wchar_t coreLibraries[MAX_LONGPATH];
+ size_t outSize;
+ if (_wgetenv_s(&outSize, coreLibraries, MAX_LONGPATH, W("CORE_LIBRARIES")) == 0 && outSize > 0)
+ {
+ wcscat_s(coreLibraries, MAX_LONGPATH, W("\\"));
+ AddFilesFromDirectoryToTPAList(coreLibraries, rgTPAExtensions, _countof(rgTPAExtensions));
+ }
+ else
+ {
+ *m_log << W("CORE_LIBRARIES not set; skipping") << Logger::endl;
+ *m_log << W("You can set the environment variable CORE_LIBRARIES to point to a") << Logger::endl;
+ *m_log << W("path containing additional platform assemblies,") << Logger::endl;
+ }
+
+ AddFilesFromDirectoryToTPAList(m_coreCLRDirectoryPath, rgTPAExtensions, _countof(rgTPAExtensions));
+ }
+
+ return m_tpaList.CStr();
+ }
+
+ // Returns the path to the host module
+ const wchar_t * GetHostPath() const {
+ return m_hostPath;
+ }
+
+ // Returns the path to the host module
+ const wchar_t * GetHostExeName() const {
+ return m_hostExeName;
+ }
+
+ // Returns the ICLRRuntimeHost2 instance, loading it from CoreCLR.dll if necessary, or nullptr on failure.
+ ICLRRuntimeHost2* GetCLRRuntimeHost() {
+ if (!m_CLRRuntimeHost) {
+
+ if (!m_coreCLRModule) {
+ *m_log << W("Unable to load ") << coreCLRDll << Logger::endl;
+ return nullptr;
+ }
+
+ *m_log << W("Finding GetCLRRuntimeHost(...)") << Logger::endl;
+
+ FnGetCLRRuntimeHost pfnGetCLRRuntimeHost =
+ (FnGetCLRRuntimeHost)::GetProcAddress(m_coreCLRModule, "GetCLRRuntimeHost");
+
+ if (!pfnGetCLRRuntimeHost) {
+ *m_log << W("Failed to find function GetCLRRuntimeHost in ") << coreCLRDll << Logger::endl;
+ return nullptr;
+ }
+
+ *m_log << W("Calling GetCLRRuntimeHost(...)") << Logger::endl;
+
+ HRESULT hr = pfnGetCLRRuntimeHost(IID_ICLRRuntimeHost2, (IUnknown**)&m_CLRRuntimeHost);
+ if (FAILED(hr)) {
+ *m_log << W("Failed to get ICLRRuntimeHost2 interface. ERRORCODE: ") << hr << Logger::endl;
+ return nullptr;
+ }
+ }
+
+ return m_CLRRuntimeHost;
+ }
+
+
+};
+
+bool TryRun(const int argc, const wchar_t* argv[], Logger &log, const bool verbose, const bool waitForDebugger, DWORD &exitCode, _In_z_ wchar_t* programPath)
+{
+ // Assume failure
+ exitCode = -1;
+
+ HostEnvironment hostEnvironment(&log);
+
+ wchar_t appPath[MAX_LONGPATH] = W("");
+ wchar_t appNiPath[MAX_LONGPATH * 2] = W("");
+ wchar_t managedAssemblyFullName[MAX_LONGPATH] = W("");
+
+ wchar_t* filePart = NULL;
+
+ if (!::GetFullPathName(programPath, MAX_LONGPATH, appPath, &filePart)) {
+ log << W("Failed to get full path: ") << programPath << Logger::endl;
+ log << W("Error code: ") << GetLastError() << Logger::endl;
+ return false;
+ }
+
+ wcscpy_s(managedAssemblyFullName, appPath);
+
+ *(filePart) = W('\0');
+
+ log << W("Loading: ") << managedAssemblyFullName << Logger::endl;
+
+ wcscpy_s(appNiPath, appPath);
+ wcscat_s(appNiPath, MAX_LONGPATH * 2, W(";"));
+ wcscat_s(appNiPath, MAX_LONGPATH * 2, appPath);
+
+ // Construct native search directory paths
+ wchar_t nativeDllSearchDirs[MAX_LONGPATH * 3];
+
+ wcscpy_s(nativeDllSearchDirs, appPath);
+ wchar_t coreLibraries[MAX_LONGPATH];
+ size_t outSize;
+ if (_wgetenv_s(&outSize, coreLibraries, MAX_LONGPATH, W("CORE_LIBRARIES")) == 0 && outSize > 0)
+ {
+ wcscat_s(nativeDllSearchDirs, MAX_LONGPATH * 3, W(";"));
+ wcscat_s(nativeDllSearchDirs, MAX_LONGPATH * 3, coreLibraries);
+ }
+ wcscat_s(nativeDllSearchDirs, MAX_LONGPATH * 3, W(";"));
+ wcscat_s(nativeDllSearchDirs, MAX_LONGPATH * 3, hostEnvironment.m_coreCLRDirectoryPath);
+
+ // Start the CoreCLR
+
+ ICLRRuntimeHost2 *host = hostEnvironment.GetCLRRuntimeHost();
+ if (!host) {
+ return false;
+ }
+
+ HRESULT hr;
+
+ log << W("Setting ICLRRuntimeHost2 startup flags") << Logger::endl;
+
+ // Default startup flags
+ hr = host->SetStartupFlags((STARTUP_FLAGS)
+ (STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN |
+ STARTUP_FLAGS::STARTUP_SINGLE_APPDOMAIN |
+ STARTUP_FLAGS::STARTUP_CONCURRENT_GC));
+ if (FAILED(hr)) {
+ log << W("Failed to set startup flags. ERRORCODE: ") << hr << Logger::endl;
+ return false;
+ }
+
+ log << W("Starting ICLRRuntimeHost2") << Logger::endl;
+
+ hr = host->Start();
+ if (FAILED(hr)) {
+ log << W("Failed to start CoreCLR. ERRORCODE: ") << hr << Logger:: endl;
+ return false;
+ }
+
+ //-------------------------------------------------------------
+
+ // Create an AppDomain
+
+ // Allowed property names:
+ // APPBASE
+ // - The base path of the application from which the exe and other assemblies will be loaded
+ //
+ // TRUSTED_PLATFORM_ASSEMBLIES
+ // - The list of complete paths to each of the fully trusted assemblies
+ //
+ // APP_PATHS
+ // - The list of paths which will be probed by the assembly loader
+ //
+ // APP_NI_PATHS
+ // - The list of additional paths that the assembly loader will probe for ngen images
+ //
+ // NATIVE_DLL_SEARCH_DIRECTORIES
+ // - The list of paths that will be probed for native DLLs called by PInvoke
+ //
+ const wchar_t *property_keys[] = {
+ W("TRUSTED_PLATFORM_ASSEMBLIES"),
+ W("APP_PATHS"),
+ W("APP_NI_PATHS"),
+ W("NATIVE_DLL_SEARCH_DIRECTORIES"),
+ W("AppDomainCompatSwitch")
+ };
+ const wchar_t *property_values[] = {
+ // TRUSTED_PLATFORM_ASSEMBLIES
+ hostEnvironment.GetTpaList(),
+ // APP_PATHS
+ appPath,
+ // APP_NI_PATHS
+ appNiPath,
+ // NATIVE_DLL_SEARCH_DIRECTORIES
+ nativeDllSearchDirs,
+ // AppDomainCompatSwitch
+ W("UseLatestBehaviorWhenTFMNotSpecified")
+ };
+
+
+ log << W("Creating an AppDomain") << Logger::endl;
+ log << W("TRUSTED_PLATFORM_ASSEMBLIES=") << property_values[0] << Logger::endl;
+ log << W("APP_PATHS=") << property_values[1] << Logger::endl;
+ log << W("APP_NI_PATHS=") << property_values[2] << Logger::endl;
+ log << W("NATIVE_DLL_SEARCH_DIRECTORIES=") << property_values[3] << Logger::endl;
+
+ DWORD domainId;
+
+ hr = host->CreateAppDomainWithManager(
+ hostEnvironment.GetHostExeName(), // The friendly name of the AppDomain
+ // Flags:
+ // APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS
+ // - By default CoreCLR only allows platform neutral assembly to be run. To allow
+ // assemblies marked as platform specific, include this flag
+ //
+ // APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP
+ // - Allows sandboxed applications to make P/Invoke calls and use COM interop
+ //
+ // APPDOMAIN_SECURITY_SANDBOXED
+ // - Enables sandboxing. If not set, the app is considered full trust
+ //
+ // APPDOMAIN_IGNORE_UNHANDLED_EXCEPTION
+ // - Prevents the application from being torn down if a managed exception is unhandled
+ //
+ APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS |
+ APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP |
+ APPDOMAIN_DISABLE_TRANSPARENCY_ENFORCEMENT,
+ NULL, // Name of the assembly that contains the AppDomainManager implementation
+ NULL, // The AppDomainManager implementation type name
+ sizeof(property_keys)/sizeof(wchar_t*), // The number of properties
+ property_keys,
+ property_values,
+ &domainId);
+
+ if (FAILED(hr)) {
+ log << W("Failed call to CreateAppDomainWithManager. ERRORCODE: ") << hr << Logger::endl;
+ return false;
+ }
+
+ if(waitForDebugger)
+ {
+ if(!IsDebuggerPresent())
+ {
+ log << W("Waiting for the debugger to attach. Press any key to continue ...") << Logger::endl;
+ getchar();
+ if (IsDebuggerPresent())
+ {
+ log << "Debugger is attached." << Logger::endl;
+ }
+ else
+ {
+ log << "Debugger failed to attach." << Logger::endl;
+ }
+ }
+ }
+
+ hr = host->ExecuteAssembly(domainId, managedAssemblyFullName, argc, (argc)?&(argv[0]):NULL, &exitCode);
+ if (FAILED(hr)) {
+ log << W("Failed call to ExecuteAssembly. ERRORCODE: ") << hr << Logger::endl;
+ return false;
+ }
+
+ log << W("App exit value = ") << exitCode << Logger::endl;
+
+
+ //-------------------------------------------------------------
+
+ // Unload the AppDomain
+
+ log << W("Unloading the AppDomain") << Logger::endl;
+
+ hr = host->UnloadAppDomain(
+ domainId,
+ true); // Wait until done
+
+ if (FAILED(hr)) {
+ log << W("Failed to unload the AppDomain. ERRORCODE: ") << hr << Logger::endl;
+ return false;
+ }
+
+
+ //-------------------------------------------------------------
+
+ // Stop the host
+
+ log << W("Stopping the host") << Logger::endl;
+
+ hr = host->Stop();
+
+ if (FAILED(hr)) {
+ log << W("Failed to stop the host. ERRORCODE: ") << hr << Logger::endl;
+ return false;
+ }
+
+ //-------------------------------------------------------------
+
+ // Release the reference to the host
+
+ log << W("Releasing ICLRRuntimeHost2") << Logger::endl;
+
+ host->Release();
+
+ return true;
+
+}
+
+void showHelp() {
+ ::wprintf(
+ W("Runs executables on CoreCLR\r\n")
+ W("\r\n")
+ W("USAGE: <program>.exe [/_d] [/_v]\r\n")
+ W("\r\n")
+ W(" Runs <program>.dll managed program on CoreCLR.\r\n")
+ W(" /_v causes verbose output to be written to the console.\r\n")
+ W(" /_d causes the process to wait for a debugger to attach before starting.\r\n")
+ W("\r\n")
+ W(" CoreCLR is searched for in %%core_root%%, then in the directory that this executable is in.\r\n")
+ W(" The program dll needs to be in the same directory as this executable.\r\n")
+ W(" The program dll needs to have main entry point.\r\n")
+ );
+}
+
+static wchar_t programPath[MAX_LONGPATH];
+
+int __cdecl wmain(const int argc, const wchar_t* argv[])
+{
+ DWORD dwModuleFileName = GetModuleFileName(NULL, programPath, MAX_LONGPATH);
+ if (dwModuleFileName == 0 || dwModuleFileName >= MAX_LONGPATH) {
+ ::wprintf(W("Failed to get the path to the current executable"));
+ return -1;
+ }
+ auto extension = wcsrchr(programPath, '.');
+ if (extension == NULL || (wcscmp(extension, L".exe") != 0)) {
+ ::wprintf(W("This executable needs to have 'exe' extension"));
+ return -1;
+ }
+
+ // Change the extension from ".exe" to ".dll"
+ extension[1] = 'd';
+ extension[2] = 'l';
+ extension[3] = 'l';
+
+ // Parse the options from the command line
+ bool verbose = false;
+ bool waitForDebugger = false;
+ bool helpRequested = false;
+ int newArgc = argc - 1;
+ const wchar_t **newArgv = argv + 1;
+
+ auto stringsEqual = [](const wchar_t * const a, const wchar_t * const b) -> bool {
+ return ::_wcsicmp(a, b) == 0;
+ };
+
+ auto tryParseOption = [&](const wchar_t* arg) -> bool {
+ if ( stringsEqual(arg, W("/_v")) || stringsEqual(arg, W("-_v")) ) {
+ verbose = true;
+ return true;
+ } else if ( stringsEqual(arg, W("/_d")) || stringsEqual(arg, W("-_d")) ) {
+ waitForDebugger = true;
+ return true;
+ } else if ( stringsEqual(arg, W("/_h")) || stringsEqual(arg, W("-_h")) ) {
+ helpRequested = true;
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ while (newArgc > 0 && tryParseOption(newArgv[0])) {
+ newArgc--;
+ newArgv++;
+ }
+
+ if (helpRequested) {
+ showHelp();
+ return -1;
+ }
+ else {
+ Logger log;
+ if (verbose) {
+ log.Enable();
+ }
+ else {
+ log.Disable();
+ }
+
+ DWORD exitCode;
+ auto success = TryRun(newArgc, newArgv, log, verbose, waitForDebugger, exitCode, programPath);
+
+ log << W("Execution ") << (success ? W("succeeded") : W("failed")) << Logger::endl;
+
+ return exitCode;
+ }
+}
diff --git a/src/coreclr/hosts/coreconsole/logger.cpp b/src/coreclr/hosts/coreconsole/logger.cpp
new file mode 100644
index 0000000000..7c13c9b547
--- /dev/null
+++ b/src/coreclr/hosts/coreconsole/logger.cpp
@@ -0,0 +1,112 @@
+// 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.
+
+#include <stdio.h>
+#include <windows.h>
+#include <Logger.h>
+#include "palclr.h"
+
+void Logger::Enable() {
+ m_isEnabled = true;
+}
+
+void Logger::Disable() {
+ m_isEnabled = false;
+}
+
+void print(const wchar_t *val) {
+ // If val is longer than 2048 characters, wprintf will refuse to print it.
+ // So write it in chunks.
+
+ const size_t chunkSize = 1024;
+
+ wchar_t chunk[chunkSize];
+
+ auto valLength = ::wcslen(val);
+
+ for (size_t i = 0 ; i < valLength ; i += chunkSize) {
+
+ ::wcsncpy_s(chunk, chunkSize, val + i, _TRUNCATE);
+
+ ::wprintf(W("%s"), chunk);
+ }
+}
+
+Logger& Logger::operator<< (bool val) {
+ if (m_isEnabled) {
+ if (val) {
+ EnsurePrefixIsPrinted();
+ print(W("true"));
+ } else {
+ EnsurePrefixIsPrinted();
+ print(W("false"));
+ }
+ }
+ return *this;
+}
+
+Logger& Logger::operator<< (int val) {
+
+ if (m_isEnabled) {
+ EnsurePrefixIsPrinted();
+ ::wprintf(W("%d"), val);
+ }
+
+ return *this;
+}
+
+#ifdef _MSC_VER
+Logger& Logger::operator<< (long val) {
+ if (m_isEnabled) {
+ EnsurePrefixIsPrinted();
+ ::wprintf(W("%d"), val);
+ }
+ return *this;
+}
+
+Logger& Logger::operator<< (unsigned long val) {
+ if (m_isEnabled) {
+ EnsurePrefixIsPrinted();
+ ::wprintf(W("%d"), val);
+ }
+ return *this;
+}
+#endif
+
+Logger& Logger::operator<< (const wchar_t *val) {
+ if (m_isEnabled) {
+ EnsurePrefixIsPrinted();
+ print(val);
+ }
+ return *this;
+}
+
+Logger& Logger::operator<< (Logger& ( *pf )(Logger&)) {
+ if (m_isEnabled) {
+ return pf(*this);
+ } else {
+ return *this;
+ }
+}
+
+void Logger::EnsurePrefixIsPrinted() {
+ if (this->m_isEnabled && this->m_prefixRequired) {
+ print(W(" HOSTLOG: "));
+ m_prefixRequired = false;
+ }
+}
+
+// Manipulators
+
+// Newline
+Logger& Logger::endl (Logger& log) {
+ if (log.m_isEnabled) {
+ log.EnsurePrefixIsPrinted();
+ print(W("\r\n"));
+ log.m_prefixRequired = true;
+ log.m_formatHRESULT = false;
+ }
+ return log;
+}
+
diff --git a/src/coreclr/hosts/coreconsole/logger.h b/src/coreclr/hosts/coreconsole/logger.h
new file mode 100644
index 0000000000..af54205396
--- /dev/null
+++ b/src/coreclr/hosts/coreconsole/logger.h
@@ -0,0 +1,54 @@
+// 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.
+
+//
+// Logger for the CoreCLR host.
+// Relies on the SYSCRT and therefore cannot use C++ libraries.
+//
+
+class Logger {
+ bool m_isEnabled;
+ bool m_prefixRequired;
+ bool m_formatHRESULT;
+
+public:
+ Logger() :
+ m_isEnabled(true),
+ m_prefixRequired(true),
+ m_formatHRESULT(false) { }
+
+ ~Logger() { }
+
+ // Enables output from the logger
+ void Enable();
+
+ // Disables output from the logger
+ void Disable();
+
+
+ Logger& operator<< (bool val);
+ Logger& operator<< (short val);
+ Logger& operator<< (unsigned short val);
+ Logger& operator<< (int val);
+ Logger& operator<< (unsigned int val);
+#ifdef _MSC_VER
+ Logger& operator<< (long val);
+ Logger& operator<< (unsigned long val);
+#endif
+ Logger& operator<< (float val);
+ Logger& operator<< (double val);
+ Logger& operator<< (long double val);
+ Logger& operator<< (const wchar_t* val);
+ Logger& operator<< (Logger& ( *pf )(Logger&));
+ static Logger& endl ( Logger& log );
+ static Logger& hresult ( Logger& log);
+
+private:
+ void EnsurePrefixIsPrinted();
+};
+
+
+
+
+
diff --git a/src/coreclr/hosts/coreconsole/native.rc b/src/coreclr/hosts/coreconsole/native.rc
new file mode 100644
index 0000000000..66900223e0
--- /dev/null
+++ b/src/coreclr/hosts/coreconsole/native.rc
@@ -0,0 +1,8 @@
+// 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.
+
+#define FX_VER_FILEDESCRIPTION_STR "Microsoft CoreCLR Program launcher\0"
+
+#include <fxver.h>
+#include <fxver.rc>
diff --git a/src/coreclr/hosts/corerun/.gitmirror b/src/coreclr/hosts/corerun/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/coreclr/hosts/corerun/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/coreclr/hosts/corerun/CMakeLists.txt b/src/coreclr/hosts/corerun/CMakeLists.txt
new file mode 100644
index 0000000000..6868bb12c3
--- /dev/null
+++ b/src/coreclr/hosts/corerun/CMakeLists.txt
@@ -0,0 +1,38 @@
+project(CoreRun)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CoreRun_SOURCES corerun.cpp logger.cpp)
+set(CoreRun_RESOURCES native.rc)
+
+add_definitions(-DFX_VER_INTERNALNAME_STR=CoreRun.exe)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ # This does not compile on Linux yet
+ if(CAN_BE_COMPILED_ON_LINUX)
+ _add_executable(CoreRun
+ ${CoreRun_SOURCES}
+ ${CoreRun_RESOURCES}
+ )
+ endif(CAN_BE_COMPILED_ON_LINUX)
+
+else()
+ _add_executable(CoreRun
+ ${CoreRun_SOURCES}
+ ${CoreRun_RESOURCES}
+ )
+
+ target_link_libraries(CoreRun
+ utilcodestaticnohost
+ advapi32.lib
+ oleaut32.lib
+ uuid.lib
+ user32.lib
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_VCRT_LIB}
+ )
+
+ # Can't compile on linux yet so only add for windows
+ install_clr(CoreRun)
+
+endif(CLR_CMAKE_PLATFORM_UNIX) \ No newline at end of file
diff --git a/src/coreclr/hosts/corerun/coreRun.nativeproj b/src/coreclr/hosts/corerun/coreRun.nativeproj
new file mode 100644
index 0000000000..87b64a5408
--- /dev/null
+++ b/src/coreclr/hosts/corerun/coreRun.nativeproj
@@ -0,0 +1,45 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>CoreRun</OutputName>
+ <TargetType>PROGRAM</TargetType>
+ <LinkSubsystem>console</LinkSubsystem>
+ <EntryPoint>wmain</EntryPoint>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE</ClAdditionalOptions>
+ <IsTestNetHost>true</IsTestNetHost>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <LinkPreCrtLibs Include="$(ClrLibPath)\utilcodestaticnohost.lib" />
+ <ProjectReference Include="$(ClrSrcDirectory)utilcode\staticnohost\staticnohost.nativeproj" />
+ </ItemGroup>
+ <ItemGroup>
+ <TargetLib Include="$(CoreSystemCrt)" />
+ <TargetLib Condition="'$(BuildForWindows7)'=='true'" Include="$(SdkLibPath)\mincore_fw.lib" />
+ <TargetLib Condition="'$(BuildForWindows7)'=='true'" Include="$(SdkLibPath)\oleaut32.lib" />
+ <TargetLib Condition="'$(BuildForWindows7)'!='true'" Include="$(SdkLibPath)\mincore.lib" />
+ <TargetLib Condition="'$(BuildForWindows7)'!='true'" Include="$(SdkLibPath)\mincore_legacy.lib" />
+ <TargetLib Condition="'$(BuildForWindows7)'!='true'" Include="$(SdkLibPath)\mincore_private.lib" />
+ <TargetLib Condition="'$(BuildForWindows7)'!='true'" Include="$(SdkLibPath)\mincore_obsolete.lib" />
+ <TargetLib Include="$(SdkLibPath)\uuid.lib" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <RCResourceFile Include="native.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <CppCompile Include="coreRun.cpp" />
+ <CppCompile Include="logger.cpp" />
+ </ItemGroup>
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp
new file mode 100644
index 0000000000..e7ddab21e9
--- /dev/null
+++ b/src/coreclr/hosts/corerun/corerun.cpp
@@ -0,0 +1,691 @@
+// 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.
+
+
+//
+// .A simple CoreCLR host that runs on CoreSystem.
+//
+
+#include "windows.h"
+#include <stdio.h>
+#include "mscoree.h"
+#include <Logger.h>
+#include "palclr.h"
+#include "sstring.h"
+
+// Utility macro for testing whether or not a flag is set.
+#define HAS_FLAG(value, flag) (((value) & (flag)) == (flag))
+
+// Environment variable for setting whether or not to use Server GC.
+// Off by default.
+static const wchar_t *serverGcVar = W("CORECLR_SERVER_GC");
+
+// Environment variable for setting whether or not to use Concurrent GC.
+// On by default.
+static const wchar_t *concurrentGcVar = W("CORECLR_CONCURRENT_GC");
+
+// The name of the CoreCLR native runtime DLL.
+static const wchar_t *coreCLRDll = W("CoreCLR.dll");
+
+// The location where CoreCLR is expected to be installed. If CoreCLR.dll isn't
+// found in the same directory as the host, it will be looked for here.
+static const wchar_t *coreCLRInstallDirectory = W("%windir%\\system32\\");
+
+// Encapsulates the environment that CoreCLR will run in, including the TPALIST
+class HostEnvironment
+{
+ // The path to this module
+ PathString m_hostPath;
+
+ // The path to the directory containing this module
+ PathString m_hostDirectoryPath;
+
+ // The name of this module, without the path
+ const wchar_t *m_hostExeName;
+
+ // The list of paths to the assemblies that will be trusted by CoreCLR
+ SString m_tpaList;
+
+ ICLRRuntimeHost2* m_CLRRuntimeHost;
+
+ HMODULE m_coreCLRModule;
+
+ Logger *m_log;
+
+
+ // Attempts to load CoreCLR.dll from the given directory.
+ // On success pins the dll, sets m_coreCLRDirectoryPath and returns the HMODULE.
+ // On failure returns nullptr.
+ HMODULE TryLoadCoreCLR(const wchar_t* directoryPath) {
+
+ StackSString coreCLRPath(directoryPath);
+ coreCLRPath.Append(coreCLRDll);
+
+ *m_log << W("Attempting to load: ") << coreCLRPath.GetUnicode() << Logger::endl;
+
+ HMODULE result = WszLoadLibraryEx(coreCLRPath, NULL, 0);
+ if (!result) {
+ *m_log << W("Failed to load: ") << coreCLRPath.GetUnicode() << Logger::endl;
+ *m_log << W("Error code: ") << GetLastError() << Logger::endl;
+ return nullptr;
+ }
+
+ // Pin the module - CoreCLR.dll does not support being unloaded.
+ HMODULE dummy_coreCLRModule;
+ if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, coreCLRPath, &dummy_coreCLRModule)) {
+ *m_log << W("Failed to pin: ") << coreCLRPath.GetUnicode() << Logger::endl;
+ return nullptr;
+ }
+
+ StackSString coreCLRLoadedPath;
+ WszGetModuleFileName(result, coreCLRLoadedPath);
+
+ *m_log << W("Loaded: ") << coreCLRLoadedPath.GetUnicode() << Logger::endl;
+
+ return result;
+ }
+
+public:
+ // The path to the directory that CoreCLR is in
+ PathString m_coreCLRDirectoryPath;
+
+ HostEnvironment(Logger *logger)
+ : m_log(logger), m_CLRRuntimeHost(nullptr) {
+
+ // Discover the path to this exe's module. All other files are expected to be in the same directory.
+ WszGetModuleFileName(::GetModuleHandleW(nullptr), m_hostPath);
+
+ // Search for the last backslash in the host path.
+ SString::CIterator lastBackslash = m_hostPath.End();
+ m_hostPath.FindBack(lastBackslash, W('\\'));
+
+ // Copy the directory path
+ m_hostDirectoryPath.Set(m_hostPath, m_hostPath.Begin(), lastBackslash + 1);
+
+ // Save the exe name
+ m_hostExeName = m_hostPath.GetUnicode(lastBackslash + 1);
+
+ *m_log << W("Host directory: ") << m_hostDirectoryPath.GetUnicode() << Logger::endl;
+
+ // Check for %CORE_ROOT% and try to load CoreCLR.dll from it if it is set
+ StackSString coreRoot;
+ m_coreCLRModule = NULL; // Initialize this here since we don't call TryLoadCoreCLR if CORE_ROOT is unset.
+ if (WszGetEnvironmentVariable(W("CORE_ROOT"), coreRoot) > 0 && coreRoot.GetCount() > 0)
+ {
+ coreRoot.Append(W('\\'));
+ m_coreCLRModule = TryLoadCoreCLR(coreRoot);
+ }
+ else
+ {
+ *m_log << W("CORE_ROOT not set; skipping") << Logger::endl;
+ *m_log << W("You can set the environment variable CORE_ROOT to point to the path") << Logger::endl;
+ *m_log << W("where CoreCLR.dll lives to help CoreRun.exe find it.") << Logger::endl;
+ }
+
+ // Try to load CoreCLR from the directory that coreRun is in
+ if (!m_coreCLRModule) {
+ m_coreCLRModule = TryLoadCoreCLR(m_hostDirectoryPath);
+ }
+
+ if (!m_coreCLRModule) {
+
+ // Failed to load. Try to load from the well-known location.
+ wchar_t coreCLRInstallPath[MAX_LONGPATH];
+ ::ExpandEnvironmentStringsW(coreCLRInstallDirectory, coreCLRInstallPath, MAX_LONGPATH);
+ m_coreCLRModule = TryLoadCoreCLR(coreCLRInstallPath);
+
+ }
+
+ if (m_coreCLRModule) {
+
+ // Save the directory that CoreCLR was found in
+ DWORD modulePathLength = WszGetModuleFileName(m_coreCLRModule, m_coreCLRDirectoryPath);
+
+ // Search for the last backslash and terminate it there to keep just the directory path with trailing slash
+ SString::Iterator lastBackslash = m_coreCLRDirectoryPath.End();
+ m_coreCLRDirectoryPath.FindBack(lastBackslash, W('\\'));
+ m_coreCLRDirectoryPath.Truncate(lastBackslash + 1);
+
+ } else {
+ *m_log << W("Unable to load ") << coreCLRDll << Logger::endl;
+ }
+ }
+
+ ~HostEnvironment() {
+ if(m_coreCLRModule) {
+ // Free the module. This is done for completeness, but in fact CoreCLR.dll
+ // was pinned earlier so this call won't actually free it. The pinning is
+ // done because CoreCLR does not support unloading.
+ ::FreeLibrary(m_coreCLRModule);
+ }
+ }
+
+ bool TPAListContainsFile(_In_z_ wchar_t* fileNameWithoutExtension, _In_reads_(countExtensions) wchar_t** rgTPAExtensions, int countExtensions)
+ {
+ if (m_tpaList.IsEmpty()) return false;
+
+ for (int iExtension = 0; iExtension < countExtensions; iExtension++)
+ {
+ StackSString fileName;
+ fileName.Append(W("\\")); // So that we don't match other files that end with the current file name
+ fileName.Append(fileNameWithoutExtension);
+ fileName.Append(rgTPAExtensions[iExtension] + 1);
+ fileName.Append(W(";")); // So that we don't match other files that begin with the current file name
+
+ if (m_tpaList.Find(m_tpaList.Begin(), fileName))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void RemoveExtensionAndNi(_In_z_ wchar_t* fileName)
+ {
+ // Remove extension, if it exists
+ wchar_t* extension = wcsrchr(fileName, W('.'));
+ if (extension != NULL)
+ {
+ extension[0] = W('\0');
+
+ // Check for .ni
+ size_t len = wcslen(fileName);
+ if (len > 3 &&
+ fileName[len - 1] == W('i') &&
+ fileName[len - 2] == W('n') &&
+ fileName[len - 3] == W('.') )
+ {
+ fileName[len - 3] = W('\0');
+ }
+ }
+ }
+
+ void AddFilesFromDirectoryToTPAList(_In_z_ const wchar_t* targetPath, _In_reads_(countExtensions) wchar_t** rgTPAExtensions, int countExtensions)
+ {
+ *m_log << W("Adding assemblies from ") << targetPath << W(" to the TPA list") << Logger::endl;
+ StackSString assemblyPath;
+ const size_t dirLength = wcslen(targetPath);
+
+ for (int iExtension = 0; iExtension < countExtensions; iExtension++)
+ {
+ assemblyPath.Set(targetPath, (DWORD)dirLength);
+ assemblyPath.Append(rgTPAExtensions[iExtension]);
+ WIN32_FIND_DATA data;
+ HANDLE findHandle = WszFindFirstFile(assemblyPath, &data);
+
+ if (findHandle != INVALID_HANDLE_VALUE) {
+ do {
+ if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ // It seems that CoreCLR doesn't always use the first instance of an assembly on the TPA list (ni's may be preferred
+ // over il, even if they appear later). So, only include the first instance of a simple assembly name to allow
+ // users the opportunity to override Framework assemblies by placing dlls in %CORE_LIBRARIES%
+
+ // ToLower for case-insensitive comparisons
+ wchar_t* fileNameChar = data.cFileName;
+ while (*fileNameChar)
+ {
+ *fileNameChar = towlower(*fileNameChar);
+ fileNameChar++;
+ }
+
+ // Remove extension
+ wchar_t fileNameWithoutExtension[MAX_PATH_FNAME];
+ wcscpy_s(fileNameWithoutExtension, MAX_PATH_FNAME, data.cFileName);
+
+ RemoveExtensionAndNi(fileNameWithoutExtension);
+
+ // Add to the list if not already on it
+ if (!TPAListContainsFile(fileNameWithoutExtension, rgTPAExtensions, countExtensions))
+ {
+ assemblyPath.Truncate(assemblyPath.Begin() + (DWORD)dirLength);
+ assemblyPath.Append(data.cFileName);
+ m_tpaList.Append(assemblyPath);
+ m_tpaList.Append(W(';'));
+ }
+ else
+ {
+ *m_log << W("Not adding ") << targetPath << data.cFileName << W(" to the TPA list because another file with the same name is already present on the list") << Logger::endl;
+ }
+ }
+ } while (0 != WszFindNextFile(findHandle, &data));
+
+ FindClose(findHandle);
+ }
+ }
+ }
+
+ // Returns the semicolon-separated list of paths to runtime dlls that are considered trusted.
+ // On first call, scans the coreclr directory for dlls and adds them all to the list.
+ const wchar_t * GetTpaList() {
+ if (m_tpaList.IsEmpty()) {
+ wchar_t *rgTPAExtensions[] = {
+ W("*.ni.dll"), // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+ W("*.dll"),
+ W("*.ni.exe"),
+ W("*.exe"),
+ W("*.ni.winmd")
+ W("*.winmd")
+ };
+
+ // Add files from %CORE_LIBRARIES% if specified
+ StackSString coreLibraries;
+ if (WszGetEnvironmentVariable(W("CORE_LIBRARIES"), coreLibraries) > 0 && coreLibraries.GetCount() > 0)
+ {
+ coreLibraries.Append(W('\\'));
+ AddFilesFromDirectoryToTPAList(coreLibraries, rgTPAExtensions, _countof(rgTPAExtensions));
+ }
+ else
+ {
+ *m_log << W("CORE_LIBRARIES not set; skipping") << Logger::endl;
+ *m_log << W("You can set the environment variable CORE_LIBRARIES to point to a") << Logger::endl;
+ *m_log << W("path containing additional platform assemblies,") << Logger::endl;
+ }
+
+ AddFilesFromDirectoryToTPAList(m_coreCLRDirectoryPath, rgTPAExtensions, _countof(rgTPAExtensions));
+ }
+
+ return m_tpaList;
+ }
+
+ // Returns the path to the host module
+ const wchar_t * GetHostPath() {
+ return m_hostPath;
+ }
+
+ // Returns the path to the host module
+ const wchar_t * GetHostExeName() {
+ return m_hostExeName;
+ }
+
+ // Returns the ICLRRuntimeHost2 instance, loading it from CoreCLR.dll if necessary, or nullptr on failure.
+ ICLRRuntimeHost2* GetCLRRuntimeHost() {
+ if (!m_CLRRuntimeHost) {
+
+ if (!m_coreCLRModule) {
+ *m_log << W("Unable to load ") << coreCLRDll << Logger::endl;
+ return nullptr;
+ }
+
+ *m_log << W("Finding GetCLRRuntimeHost(...)") << Logger::endl;
+
+ FnGetCLRRuntimeHost pfnGetCLRRuntimeHost =
+ (FnGetCLRRuntimeHost)::GetProcAddress(m_coreCLRModule, "GetCLRRuntimeHost");
+
+ if (!pfnGetCLRRuntimeHost) {
+ *m_log << W("Failed to find function GetCLRRuntimeHost in ") << coreCLRDll << Logger::endl;
+ return nullptr;
+ }
+
+ *m_log << W("Calling GetCLRRuntimeHost(...)") << Logger::endl;
+
+ HRESULT hr = pfnGetCLRRuntimeHost(IID_ICLRRuntimeHost2, (IUnknown**)&m_CLRRuntimeHost);
+ if (FAILED(hr)) {
+ *m_log << W("Failed to get ICLRRuntimeHost2 interface. ERRORCODE: ") << Logger::hresult << hr << Logger::endl;
+ return nullptr;
+ }
+ }
+
+ return m_CLRRuntimeHost;
+ }
+
+
+};
+
+// Creates the startup flags for the runtime, starting with the default startup
+// flags and adding or removing from them based on environment variables. Only
+// two environment variables are respected right now: serverGcVar, controlling
+// Server GC, and concurrentGcVar, controlling Concurrent GC.
+STARTUP_FLAGS CreateStartupFlags() {
+ auto initialFlags =
+ static_cast<STARTUP_FLAGS>(
+ STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN |
+ STARTUP_FLAGS::STARTUP_SINGLE_APPDOMAIN |
+ STARTUP_FLAGS::STARTUP_CONCURRENT_GC);
+
+ // server GC is off by default, concurrent GC is on by default.
+ auto checkVariable = [&](STARTUP_FLAGS flag, const wchar_t *var) {
+ wchar_t result[25];
+ size_t outsize;
+ if (_wgetenv_s(&outsize, result, 25, var) == 0 && outsize > 0) {
+ // set the flag if the var is present and set to 1,
+ // clear the flag if the var isp resent and set to 0.
+ // Otherwise, ignore it.
+ if (_wcsicmp(result, W("1")) == 0) {
+ initialFlags = static_cast<STARTUP_FLAGS>(initialFlags | flag);
+ } else if (_wcsicmp(result, W("0")) == 0) {
+ initialFlags = static_cast<STARTUP_FLAGS>(initialFlags & ~flag);
+ }
+ }
+ };
+
+ checkVariable(STARTUP_FLAGS::STARTUP_SERVER_GC, serverGcVar);
+ checkVariable(STARTUP_FLAGS::STARTUP_CONCURRENT_GC, concurrentGcVar);
+
+ return initialFlags;
+}
+
+bool TryRun(const int argc, const wchar_t* argv[], Logger &log, const bool verbose, const bool waitForDebugger, DWORD &exitCode)
+{
+
+ // Assume failure
+ exitCode = -1;
+
+ HostEnvironment hostEnvironment(&log);
+
+ //-------------------------------------------------------------
+
+ // Find the specified exe. This is done using LoadLibrary so that
+ // the OS library search semantics are used to find it.
+
+ const wchar_t* exeName = argc > 0 ? argv[0] : nullptr;
+ if(exeName == nullptr)
+ {
+ log << W("No exename specified.") << Logger::endl;
+ return false;
+ }
+
+ StackSString appPath;
+ StackSString appNiPath;
+ StackSString managedAssemblyFullName;
+ StackSString appLocalWinmetadata;
+
+ wchar_t* filePart = NULL;
+
+ COUNT_T size = MAX_LONGPATH;
+ wchar_t* appPathPtr = appPath.OpenUnicodeBuffer(size - 1);
+ DWORD length = WszGetFullPathName(exeName, size, appPathPtr, &filePart);
+ if (length >= size)
+ {
+ appPath.CloseBuffer();
+ size = length;
+ appPathPtr = appPath.OpenUnicodeBuffer(size - 1);
+ length = WszGetFullPathName(exeName, size, appPathPtr, &filePart);
+ }
+ if (length == 0 || length >= size) {
+ log << W("Failed to get full path: ") << exeName << Logger::endl;
+ log << W("Error code: ") << GetLastError() << Logger::endl;
+ return false;
+ }
+
+ managedAssemblyFullName.Set(appPathPtr);
+
+ *(filePart) = W('\0');
+ appPath.CloseBuffer(DWORD(filePart - appPathPtr));
+
+ log << W("Loading: ") << managedAssemblyFullName.GetUnicode() << Logger::endl;
+
+ appLocalWinmetadata.Set(appPath);
+ appLocalWinmetadata.Append(W("\\WinMetadata"));
+
+ DWORD dwAttrib = WszGetFileAttributes(appLocalWinmetadata);
+ bool appLocalWinMDexists = dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
+
+ if (!appLocalWinMDexists) {
+ appLocalWinmetadata.Clear();
+ }
+ appNiPath.Set(appPath);
+ appNiPath.Append(W("NI"));
+ appNiPath.Append(W(";"));
+ appNiPath.Append(appPath);
+
+ // Construct native search directory paths
+ StackSString nativeDllSearchDirs(appPath);
+ StackSString coreLibraries;
+ if (WszGetEnvironmentVariable(W("CORE_LIBRARIES"), coreLibraries) > 0 && coreLibraries.GetCount() > 0)
+ {
+ nativeDllSearchDirs.Append(W(";"));
+ nativeDllSearchDirs.Append(coreLibraries);
+ }
+ nativeDllSearchDirs.Append(W(";"));
+ nativeDllSearchDirs.Append(hostEnvironment.m_coreCLRDirectoryPath);
+
+ // Start the CoreCLR
+
+ ICLRRuntimeHost2 *host = hostEnvironment.GetCLRRuntimeHost();
+ if (!host) {
+ return false;
+ }
+
+ HRESULT hr;
+
+
+ STARTUP_FLAGS flags = CreateStartupFlags();
+ log << W("Setting ICLRRuntimeHost2 startup flags") << Logger::endl;
+ log << W("Server GC enabled: ") << HAS_FLAG(flags, STARTUP_FLAGS::STARTUP_SERVER_GC) << Logger::endl;
+ log << W("Concurrent GC enabled: ") << HAS_FLAG(flags, STARTUP_FLAGS::STARTUP_CONCURRENT_GC) << Logger::endl;
+
+ // Default startup flags
+ hr = host->SetStartupFlags(flags);
+ if (FAILED(hr)) {
+ log << W("Failed to set startup flags. ERRORCODE: ") << Logger::hresult << hr << Logger::endl;
+ return false;
+ }
+
+ log << W("Starting ICLRRuntimeHost2") << Logger::endl;
+
+ hr = host->Start();
+ if (FAILED(hr)) {
+ log << W("Failed to start CoreCLR. ERRORCODE: ") << Logger::hresult << hr << Logger:: endl;
+ return false;
+ }
+
+ //-------------------------------------------------------------
+
+ // Create an AppDomain
+
+ // Allowed property names:
+ // APPBASE
+ // - The base path of the application from which the exe and other assemblies will be loaded
+ //
+ // TRUSTED_PLATFORM_ASSEMBLIES
+ // - The list of complete paths to each of the fully trusted assemblies
+ //
+ // APP_PATHS
+ // - The list of paths which will be probed by the assembly loader
+ //
+ // APP_NI_PATHS
+ // - The list of additional paths that the assembly loader will probe for ngen images
+ //
+ // NATIVE_DLL_SEARCH_DIRECTORIES
+ // - The list of paths that will be probed for native DLLs called by PInvoke
+ //
+ const wchar_t *property_keys[] = {
+ W("TRUSTED_PLATFORM_ASSEMBLIES"),
+ W("APP_PATHS"),
+ W("APP_NI_PATHS"),
+ W("NATIVE_DLL_SEARCH_DIRECTORIES"),
+ W("AppDomainCompatSwitch"),
+ W("APP_LOCAL_WINMETADATA")
+ };
+ const wchar_t *property_values[] = {
+ // TRUSTED_PLATFORM_ASSEMBLIES
+ hostEnvironment.GetTpaList(),
+ // APP_PATHS
+ appPath,
+ // APP_NI_PATHS
+ appNiPath,
+ // NATIVE_DLL_SEARCH_DIRECTORIES
+ nativeDllSearchDirs,
+ // AppDomainCompatSwitch
+ W("UseLatestBehaviorWhenTFMNotSpecified"),
+ // APP_LOCAL_WINMETADATA
+ appLocalWinmetadata
+ };
+
+ log << W("Creating an AppDomain") << Logger::endl;
+ for (int idx = 0; idx < sizeof(property_keys) / sizeof(wchar_t*); idx++)
+ {
+ log << property_keys[idx] << W("=") << property_values[idx] << Logger::endl;
+ }
+
+ DWORD domainId;
+
+ hr = host->CreateAppDomainWithManager(
+ hostEnvironment.GetHostExeName(), // The friendly name of the AppDomain
+ // Flags:
+ // APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS
+ // - By default CoreCLR only allows platform neutral assembly to be run. To allow
+ // assemblies marked as platform specific, include this flag
+ //
+ // APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP
+ // - Allows sandboxed applications to make P/Invoke calls and use COM interop
+ //
+ // APPDOMAIN_SECURITY_SANDBOXED
+ // - Enables sandboxing. If not set, the app is considered full trust
+ //
+ // APPDOMAIN_IGNORE_UNHANDLED_EXCEPTION
+ // - Prevents the application from being torn down if a managed exception is unhandled
+ //
+ APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS |
+ APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP |
+ APPDOMAIN_DISABLE_TRANSPARENCY_ENFORCEMENT,
+ NULL, // Name of the assembly that contains the AppDomainManager implementation
+ NULL, // The AppDomainManager implementation type name
+ sizeof(property_keys)/sizeof(wchar_t*), // The number of properties
+ property_keys,
+ property_values,
+ &domainId);
+
+ if (FAILED(hr)) {
+ log << W("Failed call to CreateAppDomainWithManager. ERRORCODE: ") << Logger::hresult << hr << Logger::endl;
+ return false;
+ }
+
+ if(waitForDebugger)
+ {
+ if(!IsDebuggerPresent())
+ {
+ log << W("Waiting for the debugger to attach. Press any key to continue ...") << Logger::endl;
+ getchar();
+ if (IsDebuggerPresent())
+ {
+ log << "Debugger is attached." << Logger::endl;
+ }
+ else
+ {
+ log << "Debugger failed to attach." << Logger::endl;
+ }
+ }
+ }
+
+ hr = host->ExecuteAssembly(domainId, managedAssemblyFullName, argc-1, (argc-1)?&(argv[1]):NULL, &exitCode);
+ if (FAILED(hr)) {
+ log << W("Failed call to ExecuteAssembly. ERRORCODE: ") << Logger::hresult << hr << Logger::endl;
+ return false;
+ }
+
+ log << W("App exit value = ") << exitCode << Logger::endl;
+
+
+ //-------------------------------------------------------------
+
+ // Unload the AppDomain
+
+ log << W("Unloading the AppDomain") << Logger::endl;
+
+ hr = host->UnloadAppDomain(
+ domainId,
+ true); // Wait until done
+
+ if (FAILED(hr)) {
+ log << W("Failed to unload the AppDomain. ERRORCODE: ") << Logger::hresult << hr << Logger::endl;
+ return false;
+ }
+
+
+ //-------------------------------------------------------------
+
+ // Stop the host
+
+ log << W("Stopping the host") << Logger::endl;
+
+ hr = host->Stop();
+
+ if (FAILED(hr)) {
+ log << W("Failed to stop the host. ERRORCODE: ") << Logger::hresult << hr << Logger::endl;
+ return false;
+ }
+
+ //-------------------------------------------------------------
+
+ // Release the reference to the host
+
+ log << W("Releasing ICLRRuntimeHost2") << Logger::endl;
+
+ host->Release();
+
+ return true;
+
+}
+
+void showHelp() {
+ ::wprintf(
+ W("Runs executables on CoreCLR\r\n")
+ W("\r\n")
+ W("USAGE: coreRun [/d] [/v] Managed.exe\r\n")
+ W("\r\n")
+ W(" where Managed.exe is a managed executable built for CoreCLR\r\n")
+ W(" /v causes verbose output to be written to the console\r\n")
+ W(" /d causes coreRun to wait for a debugger to attach before\r\n")
+ W(" launching Managed.exe\r\n")
+ W("\r\n")
+ W(" CoreCLR is searched for in %%core_root%%, then in the directory\r\n")
+ W(" that coreRun.exe is in, then finally in %s.\r\n"),
+ coreCLRInstallDirectory
+ );
+}
+
+int __cdecl wmain(const int argc, const wchar_t* argv[])
+{
+ // Parse the options from the command line
+
+ bool verbose = false;
+ bool waitForDebugger = false;
+ bool helpRequested = false;
+ int newArgc = argc - 1;
+ const wchar_t **newArgv = argv + 1;
+
+ auto stringsEqual = [](const wchar_t * const a, const wchar_t * const b) -> bool {
+ return ::_wcsicmp(a, b) == 0;
+ };
+
+ auto tryParseOption = [&](const wchar_t* arg) -> bool {
+ if ( stringsEqual(arg, W("/v")) || stringsEqual(arg, W("-v")) ) {
+ verbose = true;
+ return true;
+ } else if ( stringsEqual(arg, W("/d")) || stringsEqual(arg, W("-d")) ) {
+ waitForDebugger = true;
+ return true;
+ } else if ( stringsEqual(arg, W("/?")) || stringsEqual(arg, W("-?")) || stringsEqual(arg, W("-h")) || stringsEqual(arg, W("--help")) ) {
+ helpRequested = true;
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ while (newArgc > 0 && tryParseOption(newArgv[0])) {
+ newArgc--;
+ newArgv++;
+ }
+
+ if (argc < 2 || helpRequested || newArgc==0) {
+ showHelp();
+ return -1;
+ } else {
+ Logger log;
+ if (verbose) {
+ log.Enable();
+ } else {
+ log.Disable();
+ }
+
+ DWORD exitCode;
+ auto success = TryRun(newArgc, newArgv, log, verbose, waitForDebugger, exitCode);
+
+ log << W("Execution ") << (success ? W("succeeded") : W("failed")) << Logger::endl;
+
+ return exitCode;
+ }
+}
diff --git a/src/coreclr/hosts/corerun/logger.cpp b/src/coreclr/hosts/corerun/logger.cpp
new file mode 100644
index 0000000000..95eb7c9561
--- /dev/null
+++ b/src/coreclr/hosts/corerun/logger.cpp
@@ -0,0 +1,269 @@
+// 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.
+
+
+
+#include <stdio.h>
+#include <windows.h>
+#include <Logger.h>
+#include "palclr.h"
+
+void Logger::Enable() {
+ m_isEnabled = true;
+}
+
+void Logger::Disable() {
+ m_isEnabled = false;
+}
+
+void print(const wchar_t *val) {
+ // If val is longer than 2048 characters, wprintf will refuse to print it.
+ // So write it in chunks.
+
+ const size_t chunkSize = 1024;
+
+ wchar_t chunk[chunkSize];
+
+ auto valLength = ::wcslen(val);
+
+ for (size_t i = 0 ; i < valLength ; i += chunkSize) {
+
+ ::wcsncpy_s(chunk, chunkSize, val + i, _TRUNCATE);
+
+ ::wprintf(W("%s"), chunk);
+ }
+}
+
+Logger& Logger::operator<< (bool val) {
+ if (m_isEnabled) {
+ if (val) {
+ EnsurePrefixIsPrinted();
+ print(W("true"));
+ } else {
+ EnsurePrefixIsPrinted();
+ print(W("false"));
+ }
+ }
+ return *this;
+}
+void PrintAsHResult(int val) {
+ const wchar_t * str = nullptr;
+
+ switch (val) {
+ case 0x00000000: str = W("S_OK"); break;
+ case 0x00000001: str = W("S_FALSE"); break;
+ case 0x8000000B: str = W("E_BOUNDS"); break;
+ case 0x8000000C: str = W("E_CHANGED_STATE"); break;
+ case 0x80000013: str = W("RO_E_CLOSED"); break;
+ case 0x8000211D: str = W("COR_E_AMBIGUOUSMATCH"); break;
+ case 0x80004001: str = W("E_NOTIMPL"); break;
+ case 0x80004002: str = W("COR_E_INVALIDCAST"); break;
+ //case 0x80004002: str = W("E_NOINTERFACE"); break;
+ case 0x80004003: str = W("COR_E_NULLREFERENCE"); break;
+ //case 0x80004003: str = W("E_POINTER"); break;
+ case 0x80004004: str = W("E_ABORT"); break;
+ case 0x80004005: str = W("E_FAIL"); break;
+ case 0x8000FFFF: str = W("E_UNEXPECTED"); break;
+ case 0x8002000a: str = W("DISP_E_OVERFLOW"); break;
+ case 0x8002000e: str = W("COR_E_TARGETPARAMCOUNT"); break;
+ case 0x80020012: str = W("COR_E_DIVIDEBYZERO"); break;
+ case 0x80028ca0: str = W("TYPE_E_TYPEMISMATCH"); break;
+ case 0x80070005: str = W("COR_E_UNAUTHORIZEDACCESS"); break;
+ //case 0x80070005: str = W("E_ACCESSDENIED"); break;
+ case 0x80070006: str = W("E_HANDLE"); break;
+ case 0x8007000B: str = W("COR_E_BADIMAGEFORMAT"); break;
+ case 0x8007000E: str = W("COR_E_OUTOFMEMORY"); break;
+ //case 0x8007000E: str = W("E_OUTOFMEMORY"); break;
+ case 0x80070057: str = W("COR_E_ARGUMENT"); break;
+ //case 0x80070057: str = W("E_INVALIDARG"); break;
+ case 0x80070216: str = W("COR_E_ARITHMETIC"); break;
+ case 0x800703E9: str = W("COR_E_STACKOVERFLOW"); break;
+ case 0x80090020: str = W("NTE_FAIL"); break;
+ case 0x80131013: str = W("COR_E_TYPEUNLOADED"); break;
+ case 0x80131014: str = W("COR_E_APPDOMAINUNLOADED"); break;
+ case 0x80131015: str = W("COR_E_CANNOTUNLOADAPPDOMAIN"); break;
+ case 0x80131040: str = W("FUSION_E_REF_DEF_MISMATCH"); break;
+ case 0x80131047: str = W("FUSION_E_INVALID_NAME"); break;
+ case 0x80131416: str = W("CORSEC_E_POLICY_EXCEPTION"); break;
+ case 0x80131417: str = W("CORSEC_E_MIN_GRANT_FAIL"); break;
+ case 0x80131418: str = W("CORSEC_E_NO_EXEC_PERM"); break;
+ //case 0x80131418: str = W("CORSEC_E_XMLSYNTAX"); break;
+ case 0x80131430: str = W("CORSEC_E_CRYPTO"); break;
+ case 0x80131431: str = W("CORSEC_E_CRYPTO_UNEX_OPER"); break;
+ case 0x80131500: str = W("COR_E_EXCEPTION"); break;
+ case 0x80131501: str = W("COR_E_SYSTEM"); break;
+ case 0x80131502: str = W("COR_E_ARGUMENTOUTOFRANGE"); break;
+ case 0x80131503: str = W("COR_E_ARRAYTYPEMISMATCH"); break;
+ case 0x80131504: str = W("COR_E_CONTEXTMARSHAL"); break;
+ case 0x80131505: str = W("COR_E_TIMEOUT"); break;
+ case 0x80131506: str = W("COR_E_EXECUTIONENGINE"); break;
+ case 0x80131507: str = W("COR_E_FIELDACCESS"); break;
+ case 0x80131508: str = W("COR_E_INDEXOUTOFRANGE"); break;
+ case 0x80131509: str = W("COR_E_INVALIDOPERATION"); break;
+ case 0x8013150A: str = W("COR_E_SECURITY"); break;
+ case 0x8013150C: str = W("COR_E_SERIALIZATION"); break;
+ case 0x8013150D: str = W("COR_E_VERIFICATION"); break;
+ case 0x80131510: str = W("COR_E_METHODACCESS"); break;
+ case 0x80131511: str = W("COR_E_MISSINGFIELD"); break;
+ case 0x80131512: str = W("COR_E_MISSINGMEMBER"); break;
+ case 0x80131513: str = W("COR_E_MISSINGMETHOD"); break;
+ case 0x80131514: str = W("COR_E_MULTICASTNOTSUPPORTED"); break;
+ case 0x80131515: str = W("COR_E_NOTSUPPORTED"); break;
+ case 0x80131516: str = W("COR_E_OVERFLOW"); break;
+ case 0x80131517: str = W("COR_E_RANK"); break;
+ case 0x80131518: str = W("COR_E_SYNCHRONIZATIONLOCK"); break;
+ case 0x80131519: str = W("COR_E_THREADINTERRUPTED"); break;
+ case 0x8013151A: str = W("COR_E_MEMBERACCESS"); break;
+ case 0x80131520: str = W("COR_E_THREADSTATE"); break;
+ case 0x80131521: str = W("COR_E_THREADSTOP"); break;
+ case 0x80131522: str = W("COR_E_TYPELOAD"); break;
+ case 0x80131523: str = W("COR_E_ENTRYPOINTNOTFOUND"); break;
+ //case 0x80131523: str = W("COR_E_UNSUPPORTEDFORMAT"); break;
+ case 0x80131524: str = W("COR_E_DLLNOTFOUND"); break;
+ case 0x80131525: str = W("COR_E_THREADSTART"); break;
+ case 0x80131527: str = W("COR_E_INVALIDCOMOBJECT"); break;
+ case 0x80131528: str = W("COR_E_NOTFINITENUMBER"); break;
+ case 0x80131529: str = W("COR_E_DUPLICATEWAITOBJECT"); break;
+ case 0x8013152B: str = W("COR_E_SEMAPHOREFULL"); break;
+ case 0x8013152C: str = W("COR_E_WAITHANDLECANNOTBEOPENED"); break;
+ case 0x8013152D: str = W("COR_E_ABANDONEDMUTEX"); break;
+ case 0x80131530: str = W("COR_E_THREADABORTED"); break;
+ case 0x80131531: str = W("COR_E_INVALIDOLEVARIANTTYPE"); break;
+ case 0x80131532: str = W("COR_E_MISSINGMANIFESTRESOURCE"); break;
+ case 0x80131533: str = W("COR_E_SAFEARRAYTYPEMISMATCH"); break;
+ case 0x80131534: str = W("COR_E_TYPEINITIALIZATION"); break;
+ case 0x80131535: str = W("COR_E_COMEMULATE"); break;
+ //case 0x80131535: str = W("COR_E_MARSHALDIRECTIVE"); break;
+ case 0x80131536: str = W("COR_E_MISSINGSATELLITEASSEMBLY"); break;
+ case 0x80131537: str = W("COR_E_FORMAT"); break;
+ case 0x80131538: str = W("COR_E_SAFEARRAYRANKMISMATCH"); break;
+ case 0x80131539: str = W("COR_E_PLATFORMNOTSUPPORTED"); break;
+ case 0x8013153A: str = W("COR_E_INVALIDPROGRAM"); break;
+ case 0x8013153B: str = W("COR_E_OPERATIONCANCELED"); break;
+ case 0x8013153D: str = W("COR_E_INSUFFICIENTMEMORY"); break;
+ case 0x8013153E: str = W("COR_E_RUNTIMEWRAPPED"); break;
+ case 0x80131541: str = W("COR_E_DATAMISALIGNED"); break;
+ case 0x80131543: str = W("COR_E_TYPEACCESS"); break;
+ case 0x80131577: str = W("COR_E_KEYNOTFOUND"); break;
+ case 0x80131578: str = W("COR_E_INSUFFICIENTEXECUTIONSTACK"); break;
+ case 0x80131600: str = W("COR_E_APPLICATION"); break;
+ case 0x80131601: str = W("COR_E_INVALIDFILTERCRITERIA"); break;
+ case 0x80131602: str = W("COR_E_REFLECTIONTYPELOAD "); break;
+ case 0x80131603: str = W("COR_E_TARGET"); break;
+ case 0x80131604: str = W("COR_E_TARGETINVOCATION"); break;
+ case 0x80131605: str = W("COR_E_CUSTOMATTRIBUTEFORMAT"); break;
+ case 0x80131622: str = W("COR_E_OBJECTDISPOSED"); break;
+ case 0x80131623: str = W("COR_E_SAFEHANDLEMISSINGATTRIBUTE"); break;
+ case 0x80131640: str = W("COR_E_HOSTPROTECTION"); break;
+ }
+
+ ::wprintf(W("0x%x"), val);
+
+ if (str != nullptr) {
+ ::wprintf(W("/%0s"), str);
+ }
+
+}
+
+Logger& Logger::operator<< (int val) {
+
+ if (m_isEnabled) {
+
+ EnsurePrefixIsPrinted();
+
+ if (m_formatHRESULT) {
+
+ PrintAsHResult(val);
+ m_formatHRESULT = false;
+
+ } else {
+
+ ::wprintf(W("%d"), val);
+
+ }
+ }
+
+ return *this;
+}
+
+#ifdef _MSC_VER
+Logger& Logger::operator<< (long val) {
+ if (m_isEnabled) {
+ EnsurePrefixIsPrinted();
+
+ if (m_formatHRESULT) {
+
+ PrintAsHResult(val);
+ m_formatHRESULT = false;
+
+ } else {
+
+ ::wprintf(W("%d"), val);
+
+ }
+ }
+ return *this;
+}
+
+Logger& Logger::operator<< (unsigned long val) {
+ if (m_isEnabled) {
+ EnsurePrefixIsPrinted();
+
+ if (m_formatHRESULT) {
+
+ PrintAsHResult(val);
+ m_formatHRESULT = false;
+
+ } else {
+
+ ::wprintf(W("%d"), val);
+
+ }
+ }
+ return *this;
+}
+#endif
+
+Logger& Logger::operator<< (const wchar_t *val) {
+ if (m_isEnabled) {
+ EnsurePrefixIsPrinted();
+ print(val);
+ }
+ return *this;
+}
+
+Logger& Logger::operator<< (Logger& ( *pf )(Logger&)) {
+ if (m_isEnabled) {
+ return pf(*this);
+ } else {
+ return *this;
+ }
+}
+
+void Logger::EnsurePrefixIsPrinted() {
+ if (this->m_isEnabled && this->m_prefixRequired) {
+ print(W(" HOSTLOG: "));
+ m_prefixRequired = false;
+ }
+}
+
+// Manipulators
+
+// Newline
+Logger& Logger::endl (Logger& log) {
+ if (log.m_isEnabled) {
+ log.EnsurePrefixIsPrinted();
+ print(W("\r\n"));
+ log.m_prefixRequired = true;
+ log.m_formatHRESULT = false;
+ }
+ return log;
+}
+
+// Format the next integer value as an HResult
+Logger& Logger::hresult (Logger& log) {
+ log.m_formatHRESULT = true;
+ return log;
+}
+
diff --git a/src/coreclr/hosts/corerun/logger.h b/src/coreclr/hosts/corerun/logger.h
new file mode 100644
index 0000000000..510f3bc97b
--- /dev/null
+++ b/src/coreclr/hosts/corerun/logger.h
@@ -0,0 +1,56 @@
+// 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.
+
+
+//
+// Logger for the CoreCLR host ccrun.
+// Relies on the SYSCRT and therefore cannot use C++ libraries.
+//
+
+
+class Logger {
+ bool m_isEnabled;
+ bool m_prefixRequired;
+ bool m_formatHRESULT;
+
+public:
+ Logger() :
+ m_isEnabled(true),
+ m_prefixRequired(true),
+ m_formatHRESULT(false) { }
+
+ ~Logger() { }
+
+ // Enables output from the logger
+ void Enable();
+
+ // Disables output from the logger
+ void Disable();
+
+
+ Logger& operator<< (bool val);
+ Logger& operator<< (short val);
+ Logger& operator<< (unsigned short val);
+ Logger& operator<< (int val);
+ Logger& operator<< (unsigned int val);
+#ifdef _MSC_VER
+ Logger& operator<< (long val);
+ Logger& operator<< (unsigned long val);
+#endif
+ Logger& operator<< (float val);
+ Logger& operator<< (double val);
+ Logger& operator<< (long double val);
+ Logger& operator<< (const wchar_t* val);
+ Logger& operator<< (Logger& ( *pf )(Logger&));
+ static Logger& endl ( Logger& log );
+ static Logger& hresult ( Logger& log);
+
+private:
+ void EnsurePrefixIsPrinted();
+};
+
+
+
+
+
diff --git a/src/coreclr/hosts/corerun/native.rc b/src/coreclr/hosts/corerun/native.rc
new file mode 100644
index 0000000000..5ad4f9893a
--- /dev/null
+++ b/src/coreclr/hosts/corerun/native.rc
@@ -0,0 +1,8 @@
+// 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.
+
+#define FX_VER_FILEDESCRIPTION_STR "Microsoft CoreCLR EXE launcher\0"
+
+#include <fxver.h>
+#include <fxver.rc>
diff --git a/src/coreclr/hosts/corerun/test.txt b/src/coreclr/hosts/corerun/test.txt
new file mode 100644
index 0000000000..037873ba55
--- /dev/null
+++ b/src/coreclr/hosts/corerun/test.txt
@@ -0,0 +1 @@
+time 2
diff --git a/src/coreclr/hosts/dirs.proj b/src/coreclr/hosts/dirs.proj
new file mode 100644
index 0000000000..11382958cb
--- /dev/null
+++ b/src/coreclr/hosts/dirs.proj
@@ -0,0 +1,19 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(FeatureCoreClr)' == 'true'">
+ <ProjectFile Include="coreRun\coreRun.nativeproj" />
+ <ProjectFile Include="fxprun\fxprun.nativeproj" />
+ <ProjectFile Include="coreconsole\CoreConsole.nativeproj" />
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/coreclr/hosts/inc/.gitmirrorall b/src/coreclr/hosts/inc/.gitmirrorall
new file mode 100644
index 0000000000..9ee5c57b99
--- /dev/null
+++ b/src/coreclr/hosts/inc/.gitmirrorall
@@ -0,0 +1 @@
+This folder will be mirrored by the Git-TFS Mirror recursively. \ No newline at end of file
diff --git a/src/coreclr/hosts/inc/coreclrhost.h b/src/coreclr/hosts/inc/coreclrhost.h
new file mode 100644
index 0000000000..f0d7952aa6
--- /dev/null
+++ b/src/coreclr/hosts/inc/coreclrhost.h
@@ -0,0 +1,50 @@
+// 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.
+
+//
+// APIs for hosting CoreCLR
+//
+
+#ifndef __CORECLR_HOST_H__
+#define __CORECLR_HOST_H__
+
+// For each hosting API, we define a function prototype and a function pointer
+// The prototype is useful for implicit linking against the dynamic coreclr
+// library and the pointer for explicit dynamic loading (dlopen, LoadLibrary)
+#define CORECLR_HOSTING_API(function, ...) \
+ extern "C" int function(__VA_ARGS__); \
+ typedef int (*function##_ptr)(__VA_ARGS__)
+
+CORECLR_HOSTING_API(coreclr_initialize,
+ const char* exePath,
+ const char* appDomainFriendlyName,
+ int propertyCount,
+ const char** propertyKeys,
+ const char** propertyValues,
+ void** hostHandle,
+ unsigned int* domainId);
+
+CORECLR_HOSTING_API(coreclr_shutdown,
+ void* hostHandle,
+ unsigned int domainId);
+
+CORECLR_HOSTING_API(coreclr_create_delegate,
+ void* hostHandle,
+ unsigned int domainId,
+ const char* entryPointAssemblyName,
+ const char* entryPointTypeName,
+ const char* entryPointMethodName,
+ void** delegate);
+
+CORECLR_HOSTING_API(coreclr_execute_assembly,
+ void* hostHandle,
+ unsigned int domainId,
+ int argc,
+ const char** argv,
+ const char* managedAssemblyPath,
+ unsigned int* exitCode);
+
+#undef CORECLR_HOSTING_API
+
+#endif // __CORECLR_HOST_H__
diff --git a/src/coreclr/hosts/osxbundlerun/.gitmirrorall b/src/coreclr/hosts/osxbundlerun/.gitmirrorall
new file mode 100644
index 0000000000..9ee5c57b99
--- /dev/null
+++ b/src/coreclr/hosts/osxbundlerun/.gitmirrorall
@@ -0,0 +1 @@
+This folder will be mirrored by the Git-TFS Mirror recursively. \ No newline at end of file
diff --git a/src/coreclr/hosts/osxbundlerun/CMakeLists.txt b/src/coreclr/hosts/osxbundlerun/CMakeLists.txt
new file mode 100644
index 0000000000..49e2248ee7
--- /dev/null
+++ b/src/coreclr/hosts/osxbundlerun/CMakeLists.txt
@@ -0,0 +1,24 @@
+project(osxbundlerun)
+
+include_directories(../unixcoreruncommon)
+
+add_compile_options(-fPIE)
+
+set(CORERUN_SOURCES
+ ../unixcoreruncommon/coreruncommon.cpp
+ osxbundlerun.cpp
+)
+
+_add_executable(osxbundlerun
+ ${CORERUN_SOURCES}
+)
+
+target_link_libraries(osxbundlerun
+ dl
+)
+
+add_dependencies(osxbundlerun
+ coreclr
+)
+
+install_clr(osxbundlerun)
diff --git a/src/coreclr/hosts/osxbundlerun/osxbundlerun.cpp b/src/coreclr/hosts/osxbundlerun/osxbundlerun.cpp
new file mode 100644
index 0000000000..d4de5c764e
--- /dev/null
+++ b/src/coreclr/hosts/osxbundlerun/osxbundlerun.cpp
@@ -0,0 +1,90 @@
+// 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.
+
+// CoreCLR boot loader for OSX app packages.
+//
+// Assumes the following app package structure
+//
+// /Contents/MacOS/yourAppName (osxbundlerun renamed to your app name)
+// /Contents/CoreClrBundle/ The CoreCLR runtime, or a symlink to it if external
+// /Contents/ManagedBundle/ Your managed assemblies, including yourAppName.exe
+//
+// Of course you can also include whatever else you might need in the app package
+//
+// Symlinking the CoreClrBundle is handy for dev/debug builds. eg:
+//
+// Contents> ln -s ~/dotnet/runtime/ CoreClrBundle
+//
+// All command line arguments are passed directly to the managed assembly's Main(args)
+// Note that args[0] will be /Contents/MacOS/yourAppName, not /Contents/ManagedBundle/yourAppName.exe
+
+
+#include <coreruncommon.h>
+#include <string>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+int corerun(const int argc, const char* argv[])
+{
+ // Make sure we have a full path for argv[0].
+ std::string argv0AbsolutePath;
+ if (!GetAbsolutePath(argv[0], argv0AbsolutePath))
+ {
+ perror("Could not get full path to current executable");
+ return -1;
+ }
+
+ // Get name of self and containing folder (typically the MacOS folder)
+ int lastSlashPos = argv0AbsolutePath.rfind('/');
+ std::string appName = argv0AbsolutePath.substr(lastSlashPos+1);
+ std::string appFolder = argv0AbsolutePath.substr(0, lastSlashPos);
+
+ // Strip off "MacOS" to get to the "Contents" folder
+ std::string contentsFolder;
+ if (!GetDirectory(appFolder.c_str(), contentsFolder))
+ {
+ perror("Could not get Contents folder");
+ return -1;
+ }
+
+ // Append standard locations
+ std::string clrFilesAbsolutePath = contentsFolder + "/CoreClrBundle";
+ std::string managedFolderAbsolutePath = contentsFolder + "/ManagedBundle/";
+ std::string managedAssemblyAbsolutePath = managedFolderAbsolutePath + appName + ".exe";
+
+ // Pass all command line arguments to managed executable
+ const char** managedAssemblyArgv = argv;
+ int managedAssemblyArgc = argc;
+
+ // Check if the specified managed assembly file exists
+ struct stat sb;
+ if (stat(managedAssemblyAbsolutePath.c_str(), &sb) == -1)
+ {
+ perror(managedAssemblyAbsolutePath.c_str());
+ return -1;
+ }
+
+ // Verify that the managed assembly path points to a file
+ if (!S_ISREG(sb.st_mode))
+ {
+ fprintf(stderr, "The specified managed assembly is not a file\n");
+ return -1;
+ }
+
+ // And go...
+ int exitCode = ExecuteManagedAssembly(
+ argv0AbsolutePath.c_str(),
+ clrFilesAbsolutePath.c_str(),
+ managedAssemblyAbsolutePath.c_str(),
+ managedAssemblyArgc,
+ managedAssemblyArgv);
+
+ return exitCode;
+}
+
+int main(const int argc, const char* argv[])
+{
+ return corerun(argc, argv);
+}
diff --git a/src/coreclr/hosts/unixcoreconsole/.gitmirrorall b/src/coreclr/hosts/unixcoreconsole/.gitmirrorall
new file mode 100644
index 0000000000..9ee5c57b99
--- /dev/null
+++ b/src/coreclr/hosts/unixcoreconsole/.gitmirrorall
@@ -0,0 +1 @@
+This folder will be mirrored by the Git-TFS Mirror recursively. \ No newline at end of file
diff --git a/src/coreclr/hosts/unixcoreconsole/CMakeLists.txt b/src/coreclr/hosts/unixcoreconsole/CMakeLists.txt
new file mode 100644
index 0000000000..8988e60dcf
--- /dev/null
+++ b/src/coreclr/hosts/unixcoreconsole/CMakeLists.txt
@@ -0,0 +1,33 @@
+project(unixcoreconsole)
+
+include_directories(../unixcoreruncommon)
+
+add_compile_options(-fPIE)
+
+set(CORECONSOLE_SOURCES
+ coreconsole.cpp
+)
+
+_add_executable(coreconsole
+ ${CORECONSOLE_SOURCES}
+)
+
+# FreeBSD and NetBSD implement dlopen(3) in libc
+if(NOT CMAKE_SYSTEM_NAME STREQUAL FreeBSD AND NOT CMAKE_SYSTEM_NAME STREQUAL NetBSD)
+ target_link_libraries(coreconsole
+ dl
+ )
+endif(NOT CMAKE_SYSTEM_NAME STREQUAL FreeBSD AND NOT CMAKE_SYSTEM_NAME STREQUAL NetBSD)
+
+# Libc turns locks into no-ops if pthread was not loaded into process yet. Loading
+# pthread by the process executable ensures that all locks are initialized properly.
+target_link_libraries(coreconsole
+ unixcoreruncommon
+ pthread
+)
+
+add_dependencies(coreconsole
+ coreclr
+)
+
+install_clr(coreconsole) \ No newline at end of file
diff --git a/src/coreclr/hosts/unixcoreconsole/coreconsole.cpp b/src/coreclr/hosts/unixcoreconsole/coreconsole.cpp
new file mode 100644
index 0000000000..e43124d0f2
--- /dev/null
+++ b/src/coreclr/hosts/unixcoreconsole/coreconsole.cpp
@@ -0,0 +1,162 @@
+// 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.
+
+//
+// A simple CoreCLR host that runs a managed binary with the same name as this executable but with the *.dll extension
+// The dll binary must contain a main entry point.
+//
+
+#include <coreruncommon.h>
+#include <string>
+#include <string.h>
+#include <sys/stat.h>
+
+// Display the help text
+void DisplayUsage()
+{
+ fprintf(
+ stderr,
+ "Runs executables on CoreCLR\n\n"
+ "Usage: <program> [OPTIONS] [ARGUMENTS]\n"
+ "Runs <program>.dll on CoreCLR.\n\n"
+ "Options:\n"
+ "-_c path to libcoreclr.so and the managed CLR assemblies.\n"
+ "-_h show this help message. \n");
+}
+
+// Parse the command line arguments
+bool ParseArguments(
+ const int argc,
+ const char* argv[],
+ const char** clrFilesPath,
+ int* managedAssemblyArgc,
+ const char*** managedAssemblyArgv)
+{
+ bool success = true;
+
+ *clrFilesPath = nullptr;
+ *managedAssemblyArgv = nullptr;
+ *managedAssemblyArgc = 0;
+
+ for (int i = 1; i < argc; i++)
+ {
+ // Check for options. Options to the Unix coreconsole are prefixed with '-_' to match the convention
+ // used in the Windows version of coreconsole.
+ if (strncmp(argv[i], "-_", 2) == 0)
+ {
+ // Path to the libcoreclr.so and the managed CLR assemblies
+ if (strcmp(argv[i], "-_c") == 0)
+ {
+ i++;
+ if (i < argc)
+ {
+ *clrFilesPath = argv[i];
+ }
+ else
+ {
+ fprintf(stderr, "Option %s: missing path\n", argv[i - 1]);
+ success = false;
+ break;
+ }
+ }
+ else if (strcmp(argv[i], "-_h") == 0)
+ {
+ DisplayUsage();
+ success = false;
+ break;
+ }
+ else
+ {
+ fprintf(stderr, "Unknown option %s\n", argv[i]);
+ success = false;
+ break;
+ }
+ }
+ else
+ {
+ // We treat everything starting from the first non-option argument as arguments
+ // to the managed assembly.
+ *managedAssemblyArgc = argc - i;
+ if (*managedAssemblyArgc != 0)
+ {
+ *managedAssemblyArgv = &argv[i];
+ }
+
+ break;
+ }
+ }
+
+ return success;
+}
+
+int main(const int argc, const char* argv[])
+{
+ // Make sure we have a full path for argv[0].
+ std::string argv0AbsolutePath;
+ std::string entryPointExecutablePath;
+
+ if (!GetEntrypointExecutableAbsolutePath(entryPointExecutablePath))
+ {
+ perror("Could not get full path to current executable");
+ return -1;
+ }
+
+ if (!GetAbsolutePath(entryPointExecutablePath.c_str(), argv0AbsolutePath))
+ {
+ perror("Could not normalize full path to current executable");
+ return -1;
+ }
+
+ // We will try to load the managed assembly with the same name as this executable
+ // but with the .dll extension.
+ std::string programPath(argv0AbsolutePath);
+ programPath.append(".dll");
+ const char* managedAssemblyAbsolutePath = programPath.c_str();
+
+ // Check if the specified managed assembly file exists
+ struct stat sb;
+ if (stat(managedAssemblyAbsolutePath, &sb) == -1)
+ {
+ perror("Managed assembly not found");
+ return -1;
+ }
+
+ // Verify that the managed assembly path points to a file
+ if (!S_ISREG(sb.st_mode))
+ {
+ fprintf(stderr, "The specified managed assembly is not a file\n");
+ return -1;
+ }
+
+ const char* clrFilesPath;
+ const char** managedAssemblyArgv;
+ int managedAssemblyArgc;
+
+ if (!ParseArguments(
+ argc,
+ argv,
+ &clrFilesPath,
+ &managedAssemblyArgc,
+ &managedAssemblyArgv
+ ))
+ {
+ // Invalid command line
+ return -1;
+ }
+
+ std::string clrFilesAbsolutePath;
+ if(!GetClrFilesAbsolutePath(argv0AbsolutePath.c_str(), clrFilesPath, clrFilesAbsolutePath))
+ {
+ return -1;
+ }
+
+ int exitCode = ExecuteManagedAssembly(
+ argv0AbsolutePath.c_str(),
+ clrFilesAbsolutePath.c_str(),
+ managedAssemblyAbsolutePath,
+ managedAssemblyArgc,
+ managedAssemblyArgv);
+
+ return exitCode;
+}
diff --git a/src/coreclr/hosts/unixcorerun/.gitmirrorall b/src/coreclr/hosts/unixcorerun/.gitmirrorall
new file mode 100644
index 0000000000..9ee5c57b99
--- /dev/null
+++ b/src/coreclr/hosts/unixcorerun/.gitmirrorall
@@ -0,0 +1 @@
+This folder will be mirrored by the Git-TFS Mirror recursively. \ No newline at end of file
diff --git a/src/coreclr/hosts/unixcorerun/CMakeLists.txt b/src/coreclr/hosts/unixcorerun/CMakeLists.txt
new file mode 100644
index 0000000000..b32c9833bf
--- /dev/null
+++ b/src/coreclr/hosts/unixcorerun/CMakeLists.txt
@@ -0,0 +1,33 @@
+project(unixcorerun)
+
+include_directories(../unixcoreruncommon)
+
+add_compile_options(-fPIE)
+
+set(CORERUN_SOURCES
+ corerun.cpp
+)
+
+_add_executable(corerun
+ ${CORERUN_SOURCES}
+)
+
+# FreeBSD and NetBSD implement dlopen(3) in libc
+if(NOT CMAKE_SYSTEM_NAME STREQUAL FreeBSD AND NOT CMAKE_SYSTEM_NAME STREQUAL NetBSD)
+ target_link_libraries(corerun
+ dl
+ )
+endif(NOT CMAKE_SYSTEM_NAME STREQUAL FreeBSD AND NOT CMAKE_SYSTEM_NAME STREQUAL NetBSD)
+
+# Libc turns locks into no-ops if pthread was not loaded into process yet. Loading
+# pthread by the process executable ensures that all locks are initialized properly.
+target_link_libraries(corerun
+ unixcoreruncommon
+ pthread
+)
+
+add_dependencies(corerun
+ coreclr
+)
+
+install_clr(corerun) \ No newline at end of file
diff --git a/src/coreclr/hosts/unixcorerun/corerun.cpp b/src/coreclr/hosts/unixcorerun/corerun.cpp
new file mode 100644
index 0000000000..da886d4338
--- /dev/null
+++ b/src/coreclr/hosts/unixcorerun/corerun.cpp
@@ -0,0 +1,162 @@
+// 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.
+
+#include <coreruncommon.h>
+#include <string>
+#include <string.h>
+#include <sys/stat.h>
+
+// Display the command line options
+void DisplayUsage()
+{
+ fprintf(
+ stderr,
+ "Usage: corerun [OPTIONS] assembly [ARGUMENTS]\n"
+ "Execute the specified managed assembly with the passed in arguments\n\n"
+ "Options:\n"
+ "-c, --clr-path path to the libcoreclr.so and the managed CLR assemblies\n");
+}
+
+// Parse the command line arguments
+bool ParseArguments(
+ const int argc,
+ const char* argv[],
+ const char** clrFilesPath,
+ const char** managedAssemblyPath,
+ int* managedAssemblyArgc,
+ const char*** managedAssemblyArgv)
+{
+ bool success = false;
+
+ *clrFilesPath = nullptr;
+ *managedAssemblyPath = nullptr;
+ *managedAssemblyArgv = nullptr;
+ *managedAssemblyArgc = 0;
+
+ // The command line must contain at least the current exe name and the managed assembly path
+ if (argc >= 2)
+ {
+ for (int i = 1; i < argc; i++)
+ {
+ // Check for an option
+ if (argv[i][0] == '-')
+ {
+ // Path to the libcoreclr.so and the managed CLR assemblies
+ if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--clr-path") == 0)
+ {
+ i++;
+ if (i < argc)
+ {
+ *clrFilesPath = argv[i];
+ }
+ else
+ {
+ fprintf(stderr, "Option %s: missing path\n", argv[i - 1]);
+ break;
+ }
+ }
+ else if (strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
+ {
+ DisplayUsage();
+ break;
+ }
+ else
+ {
+ fprintf(stderr, "Unknown option %s\n", argv[i]);
+ break;
+ }
+ }
+ else
+ {
+ // First argument that is not an option is the managed assembly to execute
+ *managedAssemblyPath = argv[i];
+
+ int managedArgvOffset = (i + 1);
+ *managedAssemblyArgc = argc - managedArgvOffset;
+ if (*managedAssemblyArgc != 0)
+ {
+ *managedAssemblyArgv = &argv[managedArgvOffset];
+ }
+ success = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ DisplayUsage();
+ }
+
+ return success;
+}
+
+int corerun(const int argc, const char* argv[])
+{
+ const char* clrFilesPath;
+ const char* managedAssemblyPath;
+ const char** managedAssemblyArgv;
+ int managedAssemblyArgc;
+
+ if (!ParseArguments(
+ argc,
+ argv,
+ &clrFilesPath,
+ &managedAssemblyPath,
+ &managedAssemblyArgc,
+ &managedAssemblyArgv))
+ {
+ // Invalid command line
+ return -1;
+ }
+
+ // Check if the specified managed assembly file exists
+ struct stat sb;
+ if (stat(managedAssemblyPath, &sb) == -1)
+ {
+ perror("Managed assembly not found");
+ return -1;
+ }
+
+ // Verify that the managed assembly path points to a file
+ if (!S_ISREG(sb.st_mode))
+ {
+ fprintf(stderr, "The specified managed assembly is not a file\n");
+ return -1;
+ }
+
+ // Make sure we have a full path for argv[0].
+ std::string argv0AbsolutePath;
+ if (!GetAbsolutePath(argv[0], argv0AbsolutePath))
+ {
+ perror("Could not get full path");
+ return -1;
+ }
+
+ std::string clrFilesAbsolutePath;
+ if(!GetClrFilesAbsolutePath(argv0AbsolutePath.c_str(), clrFilesPath, clrFilesAbsolutePath))
+ {
+ return -1;
+ }
+
+ std::string managedAssemblyAbsolutePath;
+ if (!GetAbsolutePath(managedAssemblyPath, managedAssemblyAbsolutePath))
+ {
+ perror("Failed to convert managed assembly path to absolute path");
+ return -1;
+ }
+
+ int exitCode = ExecuteManagedAssembly(
+ argv0AbsolutePath.c_str(),
+ clrFilesAbsolutePath.c_str(),
+ managedAssemblyAbsolutePath.c_str(),
+ managedAssemblyArgc,
+ managedAssemblyArgv);
+
+ return exitCode;
+}
+
+int main(const int argc, const char* argv[])
+{
+ return corerun(argc, argv);
+}
diff --git a/src/coreclr/hosts/unixcoreruncommon/.gitmirror b/src/coreclr/hosts/unixcoreruncommon/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/coreclr/hosts/unixcoreruncommon/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/coreclr/hosts/unixcoreruncommon/CMakeLists.txt b/src/coreclr/hosts/unixcoreruncommon/CMakeLists.txt
new file mode 100644
index 0000000000..a17e0a0fd6
--- /dev/null
+++ b/src/coreclr/hosts/unixcoreruncommon/CMakeLists.txt
@@ -0,0 +1,12 @@
+project(unixcoreruncommon)
+
+add_compile_options(-fPIC)
+
+_add_library(unixcoreruncommon
+ STATIC
+ coreruncommon.cpp
+)
+
+if(CLR_CMAKE_PLATFORM_LINUX)
+ target_link_libraries(unixcoreruncommon dl)
+endif(CLR_CMAKE_PLATFORM_LINUX)
diff --git a/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp
new file mode 100644
index 0000000000..b3a0c07a79
--- /dev/null
+++ b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp
@@ -0,0 +1,454 @@
+// 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.
+
+//
+// Code that is used by both the Unix corerun and coreconsole.
+//
+
+#include <cstdlib>
+#include <cstring>
+#include <assert.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <limits.h>
+#include <set>
+#include <string>
+#include <string.h>
+#include <sys/stat.h>
+#if defined(__FreeBSD__)
+#include <sys/types.h>
+#include <sys/param.h>
+#endif
+#if defined(HAVE_SYS_SYSCTL_H) || defined(__FreeBSD__)
+#include <sys/sysctl.h>
+#endif
+#include "coreruncommon.h"
+#include "coreclrhost.h"
+#include <unistd.h>
+#ifndef SUCCEEDED
+#define SUCCEEDED(Status) ((Status) >= 0)
+#endif // !SUCCEEDED
+
+// Name of the environment variable controlling server GC.
+// If set to 1, server GC is enabled on startup. If 0, server GC is
+// disabled. Server GC is off by default.
+static const char* serverGcVar = "CORECLR_SERVER_GC";
+
+#if defined(__linux__)
+#define symlinkEntrypointExecutable "/proc/self/exe"
+#elif !defined(__APPLE__)
+#define symlinkEntrypointExecutable "/proc/curproc/exe"
+#endif
+
+bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable)
+{
+ bool result = false;
+
+ entrypointExecutable.clear();
+
+ // Get path to the executable for the current process using
+ // platform specific means.
+#if defined(__linux__) || (defined(__NetBSD__) && !defined(KERN_PROC_PATHNAME))
+ // On Linux, fetch the entry point EXE absolute path, inclusive of filename.
+ char exe[PATH_MAX];
+ ssize_t res = readlink(symlinkEntrypointExecutable, exe, PATH_MAX - 1);
+ if (res != -1)
+ {
+ exe[res] = '\0';
+ entrypointExecutable.assign(exe);
+ result = true;
+ }
+ else
+ {
+ result = false;
+ }
+#elif defined(__APPLE__)
+
+ // On Mac, we ask the OS for the absolute path to the entrypoint executable
+ uint32_t lenActualPath = 0;
+ if (_NSGetExecutablePath(nullptr, &lenActualPath) == -1)
+ {
+ // OSX has placed the actual path length in lenActualPath,
+ // so re-attempt the operation
+ std::string resizedPath(lenActualPath, '\0');
+ char *pResizedPath = const_cast<char *>(resizedPath.c_str());
+ if (_NSGetExecutablePath(pResizedPath, &lenActualPath) == 0)
+ {
+ entrypointExecutable.assign(pResizedPath);
+ result = true;
+ }
+ }
+#elif defined (__FreeBSD__)
+ static const int name[] = {
+ CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1
+ };
+ char path[PATH_MAX];
+ size_t len;
+
+ len = sizeof(path);
+ if (sysctl(name, 4, path, &len, nullptr, 0) == 0)
+ {
+ entrypointExecutable.assign(path);
+ result = true;
+ }
+ else
+ {
+ // ENOMEM
+ result = false;
+ }
+#elif defined(__NetBSD__) && defined(KERN_PROC_PATHNAME)
+ static const int name[] = {
+ CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME,
+ };
+ char path[MAXPATHLEN];
+ size_t len;
+
+ len = sizeof(path);
+ if (sysctl(name, __arraycount(name), path, &len, NULL, 0) != -1)
+ {
+ entrypointExecutable.assign(path);
+ result = true;
+ }
+ else
+ {
+ result = false;
+ }
+#else
+ // On non-Mac OS, return the symlink that will be resolved by GetAbsolutePath
+ // to fetch the entrypoint EXE absolute path, inclusive of filename.
+ entrypointExecutable.assign(symlinkEntrypointExecutable);
+ result = true;
+#endif
+
+ return result;
+}
+
+bool GetAbsolutePath(const char* path, std::string& absolutePath)
+{
+ bool result = false;
+
+ char realPath[PATH_MAX];
+ if (realpath(path, realPath) != nullptr && realPath[0] != '\0')
+ {
+ absolutePath.assign(realPath);
+ // realpath should return canonicalized path without the trailing slash
+ assert(absolutePath.back() != '/');
+
+ result = true;
+ }
+
+ return result;
+}
+
+bool GetDirectory(const char* absolutePath, std::string& directory)
+{
+ directory.assign(absolutePath);
+ size_t lastSlash = directory.rfind('/');
+ if (lastSlash != std::string::npos)
+ {
+ directory.erase(lastSlash);
+ return true;
+ }
+
+ return false;
+}
+
+bool GetClrFilesAbsolutePath(const char* currentExePath, const char* clrFilesPath, std::string& clrFilesAbsolutePath)
+{
+ std::string clrFilesRelativePath;
+ const char* clrFilesPathLocal = clrFilesPath;
+ if (clrFilesPathLocal == nullptr)
+ {
+ // There was no CLR files path specified, use the folder of the corerun/coreconsole
+ if (!GetDirectory(currentExePath, clrFilesRelativePath))
+ {
+ perror("Failed to get directory from argv[0]");
+ return false;
+ }
+
+ clrFilesPathLocal = clrFilesRelativePath.c_str();
+
+ // TODO: consider using an env variable (if defined) as a fall-back.
+ // The windows version of the corerun uses core_root env variable
+ }
+
+ if (!GetAbsolutePath(clrFilesPathLocal, clrFilesAbsolutePath))
+ {
+ perror("Failed to convert CLR files path to absolute path");
+ return false;
+ }
+
+ return true;
+}
+
+void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
+{
+ const char * const tpaExtensions[] = {
+ ".ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+ ".dll",
+ ".ni.exe",
+ ".exe",
+ };
+
+ DIR* dir = opendir(directory);
+ if (dir == nullptr)
+ {
+ return;
+ }
+
+ std::set<std::string> addedAssemblies;
+
+ // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
+ // then files with .dll extension, etc.
+ for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
+ {
+ const char* ext = tpaExtensions[extIndex];
+ int extLength = strlen(ext);
+
+ struct dirent* entry;
+
+ // For all entries in the directory
+ while ((entry = readdir(dir)) != nullptr)
+ {
+ // We are interested in files only
+ switch (entry->d_type)
+ {
+ case DT_REG:
+ break;
+
+ // Handle symlinks and file systems that do not support d_type
+ case DT_LNK:
+ case DT_UNKNOWN:
+ {
+ std::string fullFilename;
+
+ fullFilename.append(directory);
+ fullFilename.append("/");
+ fullFilename.append(entry->d_name);
+
+ struct stat sb;
+ if (stat(fullFilename.c_str(), &sb) == -1)
+ {
+ continue;
+ }
+
+ if (!S_ISREG(sb.st_mode))
+ {
+ continue;
+ }
+ }
+ break;
+
+ default:
+ continue;
+ }
+
+ std::string filename(entry->d_name);
+
+ // Check if the extension matches the one we are looking for
+ int extPos = filename.length() - extLength;
+ if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
+ {
+ continue;
+ }
+
+ std::string filenameWithoutExt(filename.substr(0, extPos));
+
+ // Make sure if we have an assembly with multiple extensions present,
+ // we insert only one version of it.
+ if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
+ {
+ addedAssemblies.insert(filenameWithoutExt);
+
+ tpaList.append(directory);
+ tpaList.append("/");
+ tpaList.append(filename);
+ tpaList.append(":");
+ }
+ }
+
+ // Rewind the directory stream to be able to iterate over it for the next extension
+ rewinddir(dir);
+ }
+
+ closedir(dir);
+}
+
+int ExecuteManagedAssembly(
+ const char* currentExeAbsolutePath,
+ const char* clrFilesAbsolutePath,
+ const char* managedAssemblyAbsolutePath,
+ int managedAssemblyArgc,
+ const char** managedAssemblyArgv)
+{
+ // Indicates failure
+ int exitCode = -1;
+
+#ifdef _ARM_
+ // libunwind library is used to unwind stack frame, but libunwind for ARM
+ // does not support ARM vfpv3/NEON registers in DWARF format correctly.
+ // Therefore let's disable stack unwinding using DWARF information
+ // See https://github.com/dotnet/coreclr/issues/6698
+ //
+ // libunwind use following methods to unwind stack frame.
+ // UNW_ARM_METHOD_ALL 0xFF
+ // UNW_ARM_METHOD_DWARF 0x01
+ // UNW_ARM_METHOD_FRAME 0x02
+ // UNW_ARM_METHOD_EXIDX 0x04
+ putenv(const_cast<char *>("UNW_ARM_UNWIND_METHOD=6"));
+#endif // _ARM_
+
+ std::string coreClrDllPath(clrFilesAbsolutePath);
+ coreClrDllPath.append("/");
+ coreClrDllPath.append(coreClrDll);
+
+ if (coreClrDllPath.length() >= PATH_MAX)
+ {
+ fprintf(stderr, "Absolute path to libcoreclr.so too long\n");
+ return -1;
+ }
+
+ // Get just the path component of the managed assembly path
+ std::string appPath;
+ GetDirectory(managedAssemblyAbsolutePath, appPath);
+
+ // Construct native search directory paths
+ std::string nativeDllSearchDirs(appPath);
+ char *coreLibraries = getenv("CORE_LIBRARIES");
+ if (coreLibraries)
+ {
+ nativeDllSearchDirs.append(":");
+ nativeDllSearchDirs.append(coreLibraries);
+ }
+ nativeDllSearchDirs.append(":");
+ nativeDllSearchDirs.append(clrFilesAbsolutePath);
+
+ std::string tpaList;
+ AddFilesFromDirectoryToTpaList(clrFilesAbsolutePath, tpaList);
+
+ void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW | RTLD_LOCAL);
+ if (coreclrLib != nullptr)
+ {
+ coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)dlsym(coreclrLib, "coreclr_initialize");
+ coreclr_execute_assembly_ptr executeAssembly = (coreclr_execute_assembly_ptr)dlsym(coreclrLib, "coreclr_execute_assembly");
+ coreclr_shutdown_ptr shutdownCoreCLR = (coreclr_shutdown_ptr)dlsym(coreclrLib, "coreclr_shutdown");
+
+ if (initializeCoreCLR == nullptr)
+ {
+ fprintf(stderr, "Function coreclr_initialize not found in the libcoreclr.so\n");
+ }
+ else if (executeAssembly == nullptr)
+ {
+ fprintf(stderr, "Function coreclr_execute_assembly not found in the libcoreclr.so\n");
+ }
+ else if (shutdownCoreCLR == nullptr)
+ {
+ fprintf(stderr, "Function coreclr_shutdown not found in the libcoreclr.so\n");
+ }
+ else
+ {
+ // Check whether we are enabling server GC (off by default)
+ const char* useServerGc = std::getenv(serverGcVar);
+ if (useServerGc == nullptr)
+ {
+ useServerGc = "0";
+ }
+
+ // CoreCLR expects strings "true" and "false" instead of "1" and "0".
+ useServerGc = std::strcmp(useServerGc, "1") == 0 ? "true" : "false";
+
+ // Allowed property names:
+ // APPBASE
+ // - The base path of the application from which the exe and other assemblies will be loaded
+ //
+ // TRUSTED_PLATFORM_ASSEMBLIES
+ // - The list of complete paths to each of the fully trusted assemblies
+ //
+ // APP_PATHS
+ // - The list of paths which will be probed by the assembly loader
+ //
+ // APP_NI_PATHS
+ // - The list of additional paths that the assembly loader will probe for ngen images
+ //
+ // NATIVE_DLL_SEARCH_DIRECTORIES
+ // - The list of paths that will be probed for native DLLs called by PInvoke
+ //
+ const char *propertyKeys[] = {
+ "TRUSTED_PLATFORM_ASSEMBLIES",
+ "APP_PATHS",
+ "APP_NI_PATHS",
+ "NATIVE_DLL_SEARCH_DIRECTORIES",
+ "AppDomainCompatSwitch",
+ "System.GC.Server",
+ };
+ const char *propertyValues[] = {
+ // TRUSTED_PLATFORM_ASSEMBLIES
+ tpaList.c_str(),
+ // APP_PATHS
+ appPath.c_str(),
+ // APP_NI_PATHS
+ appPath.c_str(),
+ // NATIVE_DLL_SEARCH_DIRECTORIES
+ nativeDllSearchDirs.c_str(),
+ // AppDomainCompatSwitch
+ "UseLatestBehaviorWhenTFMNotSpecified",
+ // System.GC.Server
+ useServerGc,
+ };
+
+ void* hostHandle;
+ unsigned int domainId;
+
+ int st = initializeCoreCLR(
+ currentExeAbsolutePath,
+ "unixcorerun",
+ sizeof(propertyKeys) / sizeof(propertyKeys[0]),
+ propertyKeys,
+ propertyValues,
+ &hostHandle,
+ &domainId);
+
+ if (!SUCCEEDED(st))
+ {
+ fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st);
+ exitCode = -1;
+ }
+ else
+ {
+ st = executeAssembly(
+ hostHandle,
+ domainId,
+ managedAssemblyArgc,
+ managedAssemblyArgv,
+ managedAssemblyAbsolutePath,
+ (unsigned int*)&exitCode);
+
+ if (!SUCCEEDED(st))
+ {
+ fprintf(stderr, "coreclr_execute_assembly failed - status: 0x%08x\n", st);
+ exitCode = -1;
+ }
+
+ st = shutdownCoreCLR(hostHandle, domainId);
+ if (!SUCCEEDED(st))
+ {
+ fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st);
+ exitCode = -1;
+ }
+ }
+ }
+
+ if (dlclose(coreclrLib) != 0)
+ {
+ fprintf(stderr, "Warning - dlclose failed\n");
+ }
+ }
+ else
+ {
+ char* error = dlerror();
+ fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error);
+ }
+
+ return exitCode;
+}
diff --git a/src/coreclr/hosts/unixcoreruncommon/coreruncommon.h b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.h
new file mode 100644
index 0000000000..fb7f6730b9
--- /dev/null
+++ b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.h
@@ -0,0 +1,56 @@
+// 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.
+
+#include <string>
+
+// Get the path to entrypoint executable
+bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable);
+
+// Get absolute path from the specified path.
+// Return true in case of a success, false otherwise.
+bool GetAbsolutePath(const char* path, std::string& absolutePath);
+
+// Get directory of the specified path.
+// Return true in case of a success, false otherwise.
+bool GetDirectory(const char* absolutePath, std::string& directory);
+
+//
+// Get the absolute path to use to locate libcoreclr.so and the CLR assemblies are stored. If clrFilesPath is provided,
+// this function will return the absolute path to it. Otherwise, the directory of the current executable is used.
+//
+// Return true in case of a success, false otherwise.
+//
+bool GetClrFilesAbsolutePath(const char* currentExePath, const char* clrFilesPath, std::string& clrFilesAbsolutePath);
+
+// Add all *.dll, *.ni.dll, *.exe, and *.ni.exe files from the specified directory to the tpaList string.
+void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList);
+
+//
+// Execute the specified managed assembly.
+//
+// Parameters:
+// currentExePath - Path to the current executable
+// clrFilesAbsolutePath - Absolute path to the folder where the libcoreclr.so and CLR managed assemblies are stored
+// managedAssemblyPath - Path to the managed assembly to execute
+// managedAssemblyArgc - Number of arguments passed to the executed assembly
+// managedAssemblyArgv - Array of arguments passed to the executed assembly
+//
+// Returns:
+// ExitCode of the assembly
+//
+int ExecuteManagedAssembly(
+ const char* currentExeAbsolutePath,
+ const char* clrFilesAbsolutePath,
+ const char* managedAssemblyAbsolutePath,
+ int managedAssemblyArgc,
+ const char** managedAssemblyArgv);
+
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+static const char * const coreClrDll = "libcoreclr.dylib";
+#else
+static const char * const coreClrDll = "libcoreclr.so";
+#endif
+