From 33369e8a4bbd8fc1c993bd0366dca0b7941f9d24 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Sat, 13 Mar 2021 14:54:40 -0800 Subject: [PATCH 1/9] Remove unused files. --- src/coreclr/hosts/applydefines.pl | 128 ----------------------------- src/coreclr/hosts/corerun/test.txt | 1 - 2 files changed, 129 deletions(-) delete mode 100644 src/coreclr/hosts/applydefines.pl delete mode 100644 src/coreclr/hosts/corerun/test.txt diff --git a/src/coreclr/hosts/applydefines.pl b/src/coreclr/hosts/applydefines.pl deleted file mode 100644 index d8d4c318cb6fe..0000000000000 --- a/src/coreclr/hosts/applydefines.pl +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env perl - -use strict; - -my $sourceFile; -my $outputFile=""; -my $definesFile=""; - -#parse arguments - -if (@ARGV == 0) -{ - Usage(); -} - -my %Defines; - -# parse args - -while (@ARGV) -{ - my $nextArg=shift; - if($nextArg eq '-s') - { - NeedNextArg($nextArg, 'file name'); - $sourceFile=shift; - } - elsif ($nextArg eq '-o') - { - NeedNextArg($nextArg, 'file name'); - $outputFile=shift; - } - elsif ($nextArg eq '-f') - { - NeedNextArg($nextArg, 'file name'); - $definesFile=shift; - } - elsif ($nextArg eq '-d') - { - NeedNextArg($nextArg, 'value'); - my $customDefine=shift; - if ( $customDefine=~m/^\"?(\S+)=(\S*)\"?$/ ) - { - $Defines{$1}=$2; - } - else - { - print "-d expects name=value\n"; - Usage(); - } - } - elsif ($nextArg eq '-h') - { - Usage(); - } - else - { - print "Unknown argument '$nextArg'\n"; - Usage(); - } -} - -# check if we have what we need - -if ($sourceFile eq "" || $outputFile eq "" || $definesFile eq "") -{ - Usage(); -} - -open (SOURCEFILE,$sourceFile) or die "Cannot open $sourceFile for reading\n"; -open (DEFINESFILE,$definesFile) or die "Cannot open $definesFile for reading\n"; -open (OUTPUTFILE,"> $outputFile") or die "Cannot open $outputFile for writing\n"; - -#load defines - -while () -{ - chomp; - if (/^\s*#define\s+(\S+)\s+(\S*)\s*$/) - { - if (defined $2) - { - $Defines{$1}=$2; - } - else - { - $Defines{$1}=""; - } - } -} - -while () -{ - my $string=$_; - my $processed=""; - while ($string=~m/\$\(([^)]+)\)/) - { - if (! defined $Defines{$1}) - { - die "'$1' is not defined.\n"; - } - $string=~s/\$\(([^)]+)\)/$Defines{$1}/; - } - print OUTPUTFILE $string ; -} - - -# functions -sub Usage() -{ - print "Usage: applydefines [options]\n"; - print "\t-s \t: the source file to process\n"; - print "\t-f \t: the file containing #define settings\n"; - print "\t-o \t: the output file\n"; - print "\t-d =\t: additional define\n"; - - exit 1; -} - -sub NeedNextArg() -{ - if (@ARGV == 0) - { - print "'@_[0]' requires @_[1]\n"; - Usage(); - } -} - diff --git a/src/coreclr/hosts/corerun/test.txt b/src/coreclr/hosts/corerun/test.txt deleted file mode 100644 index 037873ba557eb..0000000000000 --- a/src/coreclr/hosts/corerun/test.txt +++ /dev/null @@ -1 +0,0 @@ -time 2 From d9b46c282f378e35cff357bc7062bcaef49a5401 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Sat, 13 Mar 2021 14:59:55 -0800 Subject: [PATCH 2/9] Rewrite corerun so that it runs on all platforms. Remove the unixcorerun implementation but fold in those its features. Attach debugger is supported on Windows and macOS. CORE_ROOT can now be set the same way on all platforms: --clr-path, CORE_ROOT, corerun dir. --- src/coreclr/hosts/CMakeLists.txt | 4 +- src/coreclr/hosts/corerun/CMakeLists.txt | 42 +- .../{unixcorerun => corerun}/config.h.in | 0 .../{unixcorerun => corerun}/configure.cmake | 0 src/coreclr/hosts/corerun/corerun.cpp | 1132 ++++++----------- src/coreclr/hosts/corerun/corerun.hpp | 617 +++++++++ src/coreclr/hosts/corerun/logger.cpp | 282 ---- src/coreclr/hosts/corerun/logger.h | 56 - src/coreclr/hosts/unixcorerun/CMakeLists.txt | 17 - src/coreclr/hosts/unixcorerun/corerun.cpp | 581 --------- 10 files changed, 1071 insertions(+), 1660 deletions(-) rename src/coreclr/hosts/{unixcorerun => corerun}/config.h.in (100%) rename src/coreclr/hosts/{unixcorerun => corerun}/configure.cmake (100%) create mode 100644 src/coreclr/hosts/corerun/corerun.hpp delete mode 100644 src/coreclr/hosts/corerun/logger.cpp delete mode 100644 src/coreclr/hosts/corerun/logger.h delete mode 100644 src/coreclr/hosts/unixcorerun/CMakeLists.txt delete mode 100644 src/coreclr/hosts/unixcorerun/corerun.cpp diff --git a/src/coreclr/hosts/CMakeLists.txt b/src/coreclr/hosts/CMakeLists.txt index e7afb6db1de45..f3a3b0c8fbd0e 100644 --- a/src/coreclr/hosts/CMakeLists.txt +++ b/src/coreclr/hosts/CMakeLists.txt @@ -1,9 +1,9 @@ include_directories(inc) if(CLR_CMAKE_HOST_WIN32) - add_subdirectory(corerun) add_subdirectory(coreshim) else(CLR_CMAKE_HOST_WIN32) add_definitions(-D_FILE_OFFSET_BITS=64) - add_subdirectory(unixcorerun) endif(CLR_CMAKE_HOST_WIN32) + +add_subdirectory(corerun) diff --git a/src/coreclr/hosts/corerun/CMakeLists.txt b/src/coreclr/hosts/corerun/CMakeLists.txt index f149bb9059e65..fc1745c2af396 100644 --- a/src/coreclr/hosts/corerun/CMakeLists.txt +++ b/src/coreclr/hosts/corerun/CMakeLists.txt @@ -1,21 +1,35 @@ -project(CoreRun) +project(corerun) set(CMAKE_INCLUDE_CURRENT_DIR ON) -add_definitions(-DFX_VER_INTERNALNAME_STR=CoreRun.exe) -_add_executable(CoreRun - corerun.cpp logger.cpp +if(CLR_CMAKE_HOST_WIN32) + add_definitions(-DFX_VER_INTERNALNAME_STR=corerun.exe) +else(CLR_CMAKE_HOST_WIN32) + include_directories("${CLR_SRC_NATIVE_DIR}/common") + include(configure.cmake) +endif(CLR_CMAKE_HOST_WIN32) + +_add_executable(corerun + corerun.cpp native.rc ) -target_link_libraries(CoreRun - utilcodestaticnohost - advapi32.lib - oleaut32.lib - uuid.lib - user32.lib - ${STATIC_MT_CRT_LIB} - ${STATIC_MT_VCRT_LIB} -) +if(CLR_CMAKE_HOST_WIN32) + target_link_libraries(corerun + advapi32.lib + oleaut32.lib + uuid.lib + user32.lib + ${STATIC_MT_CRT_LIB} + ${STATIC_MT_VCRT_LIB} + ) +else(CLR_CMAKE_HOST_WIN32) + target_link_libraries(corerun ${CMAKE_DL_LIBS}) + + # Android implements pthread natively + if(NOT CLR_CMAKE_TARGET_ANDROID) + target_link_libraries(corerun pthread) + endif() +endif(CLR_CMAKE_HOST_WIN32) -install_clr(TARGETS CoreRun) +install_clr(TARGETS corerun) diff --git a/src/coreclr/hosts/unixcorerun/config.h.in b/src/coreclr/hosts/corerun/config.h.in similarity index 100% rename from src/coreclr/hosts/unixcorerun/config.h.in rename to src/coreclr/hosts/corerun/config.h.in diff --git a/src/coreclr/hosts/unixcorerun/configure.cmake b/src/coreclr/hosts/corerun/configure.cmake similarity index 100% rename from src/coreclr/hosts/unixcorerun/configure.cmake rename to src/coreclr/hosts/corerun/configure.cmake diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index 2db9d3606765c..cd3b50f0b537b 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -1,544 +1,274 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +// Runtime headers +#include -// -// .A simple CoreCLR host that runs on CoreSystem. -// - -#include "windows.h" -#include -#include "mscoree.h" -#include "coreclrhost.h" -#include -#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("COMPlus_gcServer"); +#include "corerun.hpp" -// Environment variable for setting whether or not to use Concurrent GC. -// On by default. -static const wchar_t *concurrentGcVar = W("COMPlus_gcConcurrent"); +using char_t = pal::char_t; +using string_t = pal::string_t; -// 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 +struct configuration { - // 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 - SString m_hostExeName; - - // The list of paths to the assemblies that will be trusted by CoreCLR - SString m_tpaList; - - coreclr_initialize_ptr m_CLRRuntimeHostInitialize; - - coreclr_execute_assembly_ptr m_CLRRuntimeHostExecute; - - coreclr_shutdown_2_ptr m_CLRRuntimeHostShutdown; - - 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); + configuration() = default; + configuration(const configuration&) = delete; + configuration(configuration&&) = delete; + configuration& operator=(const configuration&) = delete; + configuration& operator=(configuration&&) = delete; - *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; + ~configuration() + { + for (int i = 0; i < entry_assembly_argc; ++i) + { + ::free((void*)entry_assembly_argv[i]); } - - StackSString coreCLRLoadedPath; - WszGetModuleFileName(result, coreCLRLoadedPath); - - *m_log << W("Loaded: ") << coreCLRLoadedPath.GetUnicode() << Logger::endl; - - return result; + ::free(entry_assembly_argv); } -public: - // The path to the directory that CoreCLR is in - PathString m_coreCLRDirectoryPath; - - HostEnvironment(Logger *logger) - : m_CLRRuntimeHostInitialize(nullptr) - , m_CLRRuntimeHostExecute(nullptr) - , m_CLRRuntimeHostShutdown(nullptr) - , m_log(logger) { + // + // Settings + // - // Discover the path to this exe's module. All other files are expected to be in the same directory. - WszGetModuleFileName(::GetModuleHandleW(nullptr), m_hostPath); + // CLR path - user supplied location of coreclr binary and managed assemblies. + string_t clr_path; - // Search for the last backslash in the host path. - SString::CIterator lastBackslash = m_hostPath.End(); - m_hostPath.FindBack(lastBackslash, W('\\')); + // The full path to the Supplied managed entry assembly. + string_t entry_assembly_fullpath; - // Copy the directory path - m_hostDirectoryPath.Set(m_hostPath, m_hostPath.Begin(), lastBackslash + 1); + // Arguments to pass to managed entry assembly. + int entry_assembly_argc; + const char_t** entry_assembly_argv; - // Save the exe name - m_hostExeName = m_hostPath.GetUnicode(lastBackslash + 1); + // Wait for debugger to be attached. + bool wait_to_debug; - *m_log << W("Host directory: ") << m_hostDirectoryPath.GetUnicode() << Logger::endl; + // Verbose output from corerun. + bool verbose; - // 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); - - } + // Perform self test. + bool self_test; +}; - if (m_coreCLRModule) - { +namespace envvar +{ + // Points to a path containing the CoreCLR binary. + const char_t* coreRoot = W("CORE_ROOT"); - // Save the directory that CoreCLR was found in - DWORD modulePathLength = WszGetModuleFileName(m_coreCLRModule, m_coreCLRDirectoryPath); + // Points to a path containing additional platform assemblies. + const char_t* coreLibraries = W("CORE_LIBRARIES"); - // 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); + // Variable used to preload a mock hostpolicy for testing. + const char_t* mockHostPolicy = W("MOCK_HOSTPOLICY"); - m_CLRRuntimeHostInitialize = (coreclr_initialize_ptr)GetProcAddress(m_coreCLRModule, "coreclr_initialize"); - if (!m_CLRRuntimeHostInitialize) - { - *m_log << W("Failed to find function coreclr_initialize in ") << coreCLRDll << Logger::endl; - } + // 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. + const char_t* serverGc = W("COMPlus_gcServer"); - m_CLRRuntimeHostExecute = (coreclr_execute_assembly_ptr)GetProcAddress(m_coreCLRModule, "coreclr_execute_assembly"); - if (!m_CLRRuntimeHostExecute) - { - *m_log << W("Failed to find function coreclr_execute_assembly in ") << coreCLRDll << Logger::endl; - } + // Environment variable for setting whether or not to use Concurrent GC. + // On by default. + const char_t* concurrentGc = W("COMPlus_gcConcurrent"); - m_CLRRuntimeHostShutdown = (coreclr_shutdown_2_ptr)GetProcAddress(m_coreCLRModule, "coreclr_shutdown_2"); - if (!m_CLRRuntimeHostShutdown) - { - *m_log << W("Failed to find function coreclr_shutdown_2 in ") << coreCLRDll << Logger::endl; - } - } - else - { - *m_log << W("Unable to load ") << coreCLRDll << Logger::endl; - } - } + // Name of environment variable to control "System.Globalization.Invariant" + // Set to 1 for Globalization Invariant mode to be true. Default is false. + const char_t* globalizationInvariant = W("CORECLR_GLOBAL_INVARIANT"); +} - bool TPAListContainsFile(_In_z_ wchar_t* fileNameWithoutExtension, _In_reads_(countExtensions) const wchar_t** rgTPAExtensions, int countExtensions) +static void wait_for_debugger() +{ + if (!pal::is_debugger_attached()) { - if (m_tpaList.IsEmpty()) return false; - - for (int iExtension = 0; iExtension < countExtensions; iExtension++) + pal::fprintf(stdout, W("Waiting for the debugger to attach. Press any key to continue ...\n")); + (void)getchar(); + if (pal::is_debugger_attached()) { - 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; - } + pal::fprintf(stdout, W("Debugger is attached.\n")); } - return false; - } - - void RemoveExtensionAndNi(_In_z_ wchar_t* fileName) - { - // Remove extension, if it exists - wchar_t* extension = wcsrchr(fileName, W('.')); - if (extension != NULL) + else { - 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'); - } + pal::fprintf(stdout, W("Debugger failed to attach.\n")); } } +} - void AddFilesFromDirectoryToTPAList(_In_z_ const wchar_t* targetPath, _In_reads_(countExtensions) const wchar_t** rgTPAExtensions, int countExtensions) +// N.B. It seems that CoreCLR doesn't always use the first instance of an assembly on the TPA list +// (for example, ni's may be preferred over il, even if they appear later). Therefore, when building +// the TPA 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%. +static string_t build_tpa(const string_t& core_root, const string_t& core_libraries) +{ + static const char_t* const tpa_extensions[] = { - *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 SString& GetTpaList() { - if (m_tpaList.IsEmpty()) { - const 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 - 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)); - } + 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"), + nullptr + }; - return m_tpaList; - } + std::set name_set; + pal::stringstream_t tpa_list; - // Returns the path to the host module - const SString& GetHostPath() { - return m_hostPath; - } + // Iterate over all extensions. + for (const char_t* const* curr_ext = tpa_extensions; *curr_ext != nullptr; ++curr_ext) + { + const char_t* ext = *curr_ext; + const size_t ext_len = pal::strlen(ext); - // Returns the path to the host module - const SString& GetHostExeName() { - return m_hostExeName; - } + // Iterate over all supplied directories. + for (const string_t& dir : { core_libraries, core_root }) + { + if (dir.empty()) + continue; - bool IsHostLoaded() { - return m_coreCLRModule && m_CLRRuntimeHostInitialize && m_CLRRuntimeHostExecute && m_CLRRuntimeHostShutdown; - } + assert(dir.back() == pal::dir_delim); + string_t tmp = pal::build_file_list(dir, ext, [&](const char_t* file) + { + string_t file_local{ file }; - HRESULT InitializeHost( - const char *exePath, - const char *appDomainFriendlyName, - int propertyCount, - const char **propertyKeys, - const char **propertyValues, - void **hostHandle, - unsigned int *domainId) - { - if (m_CLRRuntimeHostInitialize) - { - return m_CLRRuntimeHostInitialize(exePath, appDomainFriendlyName, propertyCount, propertyKeys, propertyValues, hostHandle, domainId); - } - else - { - return E_FAIL; - } - } + // Strip the extension. + if (pal::string_ends_with(file_local, ext_len, ext)) + file_local = file_local.substr(0, file_local.length() - ext_len); - HRESULT ExecuteAssembly( - void *hostHandle, - unsigned int domainId, - int argc, - const char **argv, - const char *managedAssemblyPath, - unsigned int *exitCode) - { - if (m_CLRRuntimeHostExecute) - { - return m_CLRRuntimeHostExecute(hostHandle, domainId, argc, argv, managedAssemblyPath, exitCode); - } - else - { - return E_FAIL; - } - } + // Return true if the file is new. + return name_set.insert(file_local).second; + }); - HRESULT ShutdownHost( - void *hostHandle, - unsigned int domainId, - int *latchedExitCode) - { - if (m_CLRRuntimeHostShutdown) - { - return m_CLRRuntimeHostShutdown(hostHandle, domainId, latchedExitCode); - } - else - { - return E_FAIL; + // Add to the TPA. + tpa_list << tmp; } } -}; -// 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_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(initialFlags | flag); - } else if (_wcsicmp(result, W("0")) == 0) { - initialFlags = static_cast(initialFlags & ~flag); - } - } - }; + return tpa_list.str(); +} - checkVariable(STARTUP_FLAGS::STARTUP_SERVER_GC, serverGcVar); - checkVariable(STARTUP_FLAGS::STARTUP_CONCURRENT_GC, concurrentGcVar); +static bool try_get_export(pal::mod_t mod, const char* symbol, void** fptr) +{ + assert(mod != nullptr && symbol != nullptr && fptr != nullptr); + *fptr = pal::get_module_symbol(mod, symbol); + if (*fptr != nullptr) + return true; - return initialFlags; + pal::fprintf(stderr, W("Export '%s' not found.\n"), symbol); + return false; } -// Class used to manage activation context. -// See: https://docs.microsoft.com/en-us/windows/desktop/SbsCs/using-the-activation-context-api -class ActivationContext +static const char* get_envvar_as_boolean(const char_t* var, bool def = false) { -public: - // logger - Logger to record errors - // assemblyPath - Assembly containing activation context manifest - ActivationContext(Logger &logger, _In_z_ const WCHAR *assemblyPath) - : _logger{ logger } - , _actCxt{ INVALID_HANDLE_VALUE } - , _actCookie{} - { - ACTCTX cxt{}; - cxt.cbSize = sizeof(cxt); - cxt.dwFlags = (ACTCTX_FLAG_APPLICATION_NAME_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID); - cxt.lpSource = assemblyPath; - cxt.lpResourceName = MAKEINTRESOURCEW(1); // The CreateProcess manifest which contains the context details - - _actCxt = ::CreateActCtxW(&cxt); - if (_actCxt == INVALID_HANDLE_VALUE) - { - DWORD err = ::GetLastError(); - if (err == ERROR_RESOURCE_TYPE_NOT_FOUND) - { - _logger << W("Assembly does not contain a manifest for activation") << Logger::endl; - } - else - { - _logger << W("Activation Context creation failed. Error Code: ") << Logger::hresult << err << Logger::endl; - } - } - else - { - BOOL res = ::ActivateActCtx(_actCxt, &_actCookie); - if (res == FALSE) - _logger << W("Failed to activate Activation Context. Error Code: ") << Logger::hresult << ::GetLastError() << Logger::endl; - } - } + string_t val = pal::getenv(var); + if (val.empty()) + val = def ? W("1") : W("0"); + + // CoreCLR expects strings "true" and "false" instead of "1" and "0". + return (val.compare(W("1")) == 0 || val.compare(W("true")) == 0 || val.compare(W("TRUE")) == 0) + ? "true" + : "false"; +} - ~ActivationContext() - { - if (_actCookie != ULONG_PTR{}) - { - BOOL res = ::DeactivateActCtx(0, _actCookie); - if (res == FALSE) - _logger << W("Failed to de-activate Activation Context. Error Code: ") << Logger::hresult << ::GetLastError() << Logger::endl; - } +// The current CoreCLR instance details. +static void* CurrentClrInstance; +static unsigned int CurrentAppDomainId; - if (_actCxt != INVALID_HANDLE_VALUE) - ::ReleaseActCtx(_actCxt); - } +static int run(const configuration& config) +{ + platform_specific_actions actions; -private: - Logger &_logger; - HANDLE _actCxt; - ULONG_PTR _actCookie; -}; + // Check if debugger attach scenario was requested. + if (config.wait_to_debug) + wait_for_debugger(); -class ClrInstanceDetails -{ - static void * _currentClrInstance; - static unsigned int _currentAppDomainId; + string_t exe_path = pal::get_exe_path(); -public: // static - static HRESULT GetDetails(void **clrInstance, unsigned int *appDomainId) + // Determine the managed application's path. + string_t app_path; { - *clrInstance = _currentClrInstance; - *appDomainId = _currentAppDomainId; - return S_OK; + string_t file; + pal::split_path_to_dir_filename(config.entry_assembly_fullpath, app_path, file); + pal::ensure_trailing_delimiter(app_path); } -public: - ClrInstanceDetails(void *clrInstance, unsigned int appDomainId) - { - _currentClrInstance = clrInstance; - _currentAppDomainId = appDomainId; - } + // Define the NI app_path. + string_t app_path_ni = app_path + W("NI"); + pal::ensure_trailing_delimiter(app_path_ni); + app_path_ni.append(1, pal::env_path_delim); + app_path_ni.append(app_path); - ~ClrInstanceDetails() + // Accumulate path for native search path. + pal::stringstream_t native_search_dirs; + native_search_dirs << app_path << pal::env_path_delim; + + // CORE_LIBRARIES + string_t core_libs = pal::getenv(envvar::coreLibraries); + if (!core_libs.empty() && core_libs != app_path) { - _currentClrInstance = nullptr; - _currentAppDomainId = 0; + pal::ensure_trailing_delimiter(core_libs); + native_search_dirs << core_libs << pal::env_path_delim; } -}; -void * ClrInstanceDetails::_currentClrInstance; -unsigned int ClrInstanceDetails::_currentAppDomainId; + // Determine CORE_ROOT. + // Check if the path is user supplied and if not try + // the CORE_ROOT environment variable. + string_t core_root = !config.clr_path.empty() + ? config.clr_path + : pal::getenv(envvar::coreRoot); -extern "C" __declspec(dllexport) HRESULT __cdecl GetCurrentClrDetails(void **clrInstance, unsigned int *appDomainId) -{ - return ClrInstanceDetails::GetDetails(clrInstance, appDomainId); -} - -bool TryLoadHostPolicy(StackSString& hostPolicyPath) -{ - const WCHAR *hostpolicyName = W("hostpolicy.dll"); - HMODULE hMod = ::GetModuleHandleW(hostpolicyName); - if (hMod != nullptr) + // If CORE_ROOT wasn't supplied use the exe binary path, otherwise + // ensure path is valid and add to native search path. + if (core_root.empty()) { - return true; + string_t file; + pal::split_path_to_dir_filename(exe_path, core_root, file); + pal::ensure_trailing_delimiter(core_root); } - // Check if a hostpolicy exists and if it does, load it. - if (INVALID_FILE_ATTRIBUTES != ::GetFileAttributesW(hostPolicyPath.GetUnicode())) + else { - hMod = ::LoadLibraryExW(hostPolicyPath.GetUnicode(), nullptr, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + pal::ensure_trailing_delimiter(core_root); + native_search_dirs << core_root << pal::env_path_delim; } - return hMod != nullptr; -} + string_t tpa_list = build_tpa(core_root, core_libs); -HRESULT InitializeHost( - Logger& log, - HostEnvironment& host, - const SString& appPath, - const SString& appNiPath, - const SString& nativeDllSearchDirs, - void **hostHandle, - unsigned int *domainId) -{ - StackScratchBuffer hostExeNameUTF8; - StackScratchBuffer tpaListUTF8; - StackScratchBuffer appPathUTF8; - StackScratchBuffer appNiPathUTF8; - StackScratchBuffer nativeDllSearchDirsUTF8; + { + // Load hostpolicy if requested. + string_t mock_hostpolicy = pal::getenv(envvar::mockHostPolicy); + if (!mock_hostpolicy.empty() + && !pal::try_load_hostpolicy(mock_hostpolicy)) + { + return -1; + } + } + + actions.before_coreclr_load(); - STARTUP_FLAGS flags = CreateStartupFlags(); + // Attempt to load CoreCLR. + pal::mod_t coreclr_mod; + if (!pal::try_load_coreclr(core_root, coreclr_mod)) + { + return -1; + } - //------------------------------------------------------------- + // Get CoreCLR exports + coreclr_initialize_ptr coreclr_init_func = nullptr; + coreclr_execute_assembly_ptr coreclr_execute_func = nullptr; + coreclr_shutdown_2_ptr coreclr_shutdown2_func = nullptr; + if (!try_get_export(coreclr_mod, "coreclr_initialize", (void**)&coreclr_init_func) + || !try_get_export(coreclr_mod, "coreclr_execute_assembly", (void**)&coreclr_execute_func) + || !try_get_export(coreclr_mod, "coreclr_shutdown_2", (void**)&coreclr_shutdown2_func)) + { + return -1; + } - // Initialize host. + // Construct CoreCLR properties. + pal::string_utf8_t tpa_list_utf8 = pal::convert_to_utf8(std::move(tpa_list)); + pal::string_utf8_t app_path_utf8 = pal::convert_to_utf8(std::move(app_path)); + pal::string_utf8_t app_path_ni_utf8 = pal::convert_to_utf8(std::move(app_path_ni)); + pal::string_utf8_t native_search_dirs_utf8 = pal::convert_to_utf8(native_search_dirs.str()); + const char* enable_server_gc = get_envvar_as_boolean(envvar::serverGc); + const char* enable_concurrent_gc = get_envvar_as_boolean(envvar::concurrentGc, true /* default */); + const char* enable_globalization_invariant = get_envvar_as_boolean(envvar::globalizationInvariant); // 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 @@ -551,308 +281,294 @@ HRESULT InitializeHost( // // NATIVE_DLL_SEARCH_DIRECTORIES // - The list of paths that will be probed for native DLLs called by PInvoke - // - const char* property_keys[] = { + const char* propertyKeys[] = + { "TRUSTED_PLATFORM_ASSEMBLIES", "APP_PATHS", "APP_NI_PATHS", "NATIVE_DLL_SEARCH_DIRECTORIES", "System.GC.Server", - "System.GC.Concurrent" + "System.GC.Concurrent", + "System.Globalization.Invariant", }; - const char* property_values[] = { + const char* propertyValues[] = + { // TRUSTED_PLATFORM_ASSEMBLIES - host.GetTpaList().GetUTF8(tpaListUTF8), + tpa_list_utf8.c_str(), // APP_PATHS - appPath.GetUTF8(appPathUTF8), + app_path_utf8.c_str(), // APP_NI_PATHS - appNiPath.GetUTF8(appNiPathUTF8), + app_path_ni_utf8.c_str(), // NATIVE_DLL_SEARCH_DIRECTORIES - nativeDllSearchDirs.GetUTF8(nativeDllSearchDirsUTF8), + native_search_dirs_utf8.c_str(), // System.GC.Server - flags & STARTUP_SERVER_GC ? "true" : "false", + enable_server_gc, // System.GC.Concurrent - flags & STARTUP_CONCURRENT_GC ? "true" : "false" + enable_concurrent_gc, + // System.Globalization.Invariant + enable_globalization_invariant, }; - log << W("Initialize host") << Logger::endl; - for (int idx = 0; idx < ARRAYSIZE(property_keys); idx++) + // Construct arguments + pal::string_utf8_t exe_path_utf8 = pal::convert_to_utf8(std::move(exe_path)); + std::vector argv_lifetime; + pal::malloc_ptr argv_utf8{ pal::convert_argv_to_utf8(config.entry_assembly_argc, config.entry_assembly_argv, argv_lifetime) }; + pal::string_utf8_t entry_assembly_utf8 = pal::convert_to_utf8(config.entry_assembly_fullpath.c_str()); + + int result; + result = coreclr_init_func( + exe_path_utf8.c_str(), + "corerun", + sizeof(propertyKeys) / sizeof(propertyKeys[0]), + propertyKeys, + propertyValues, + &CurrentClrInstance, + &CurrentAppDomainId); + if (FAILED(result)) { - log << property_keys[idx] << W("=") << property_values[idx] << Logger::endl; - } - - HRESULT hr = host.InitializeHost( - NULL, - host.GetHostExeName().GetUTF8(hostExeNameUTF8), - ARRAYSIZE(property_keys), - property_keys, - property_values, - hostHandle, - domainId); - - if (FAILED(hr)) - { - log << W("Failed to initialize host. ERRORCODE: ") << Logger::hresult << hr << Logger::endl; + pal::fprintf(stderr, W("coreclr_initialize failed - Error: 0x%08x\n"), result); + return -1; } - return hr; -} - -HRESULT ExecuteAssembly( - Logger& log, - HostEnvironment& host, - const SString& managedAssemblyFullName, - void *hostHandle, - unsigned int domainId, - int argc, - const wchar_t *argv[], - unsigned int *exitCode) -{ - NewArrayHolder argvUTF8Holder; - NewArrayHolder argvUTF8Values; - if (argc && argv) + int exit_code; { - argvUTF8Holder = new (nothrow) const char*[argc]; - argvUTF8Values = new (nothrow) SString[argc]; - if (argvUTF8Holder.GetValue() && argvUTF8Values.GetValue()) + actions.before_execute_assembly(config.entry_assembly_fullpath); + + result = coreclr_execute_func( + CurrentClrInstance, + CurrentAppDomainId, + config.entry_assembly_argc, + argv_utf8.get(), + entry_assembly_utf8.c_str(), + (uint32_t*)&exit_code); + if (FAILED(result)) { - StackSString conversionBuffer; - for (int i = 0; i < argc; i++) - { - conversionBuffer.Set(argv[i]); - conversionBuffer.ConvertToUTF8(argvUTF8Values[i]); - argvUTF8Holder[i] = argvUTF8Values[i].GetUTF8NoConvert(); - } + pal::fprintf(stderr, W("coreclr_execute_assembly failed - Error: 0x%08x\n"), result); + return -1; } - } - ActivationContext cxt{ log, managedAssemblyFullName.GetUnicode() }; - ClrInstanceDetails current{ hostHandle, domainId }; - - log << W("Executing assembly: ") << managedAssemblyFullName << Logger::endl; + actions.after_execute_assembly(); + } - StackScratchBuffer managedAssemblyFullNameUTF8; - HRESULT hr = host.ExecuteAssembly(hostHandle, domainId, argc, argvUTF8Holder.GetValue(), managedAssemblyFullName.GetUTF8(managedAssemblyFullNameUTF8), exitCode); - if (FAILED(hr)) + int latched_exit_code = 0; + result = coreclr_shutdown2_func(CurrentClrInstance, CurrentAppDomainId, &latched_exit_code); + if (FAILED(result)) { - log << W("Failed call to ExecuteAssembly. ERRORCODE: ") << Logger::hresult << hr << Logger::endl; + pal::fprintf(stderr, W("coreclr_shutdown_2 failed - Error: 0x%08x\n"), result); + exit_code = -1; } - return hr; + if (exit_code != -1) + exit_code = latched_exit_code; + + return exit_code; } -HRESULT ShutdownHost( - Logger& log, - HostEnvironment& host, - void *hostHandle, - unsigned int domainId, - unsigned int *exitCode) +// Display the command line options +static void display_usage() { - log << W("Shutting down host") << Logger::endl; - - HRESULT hr = host.ShutdownHost(hostHandle, domainId, (int *)exitCode); - if (FAILED(hr)) - { - log << W("Failed to shutdown host. ERRORCODE: ") << Logger::hresult << hr << Logger::endl; - } - - return hr; + pal::fprintf( + stderr, + W("USAGE: corerun [OPTIONS] assembly [ARGUMENTS]\n") + W("\n") + W("Execute the managed assembly with the passed in arguments\n") + W("\n") + W("Options:\n") + W(" -c, --clr-path - path to CoreCLR binary and managed CLR assemblies\n") + W(" -v, --verbose - causes verbose output to be written to the console\n") + W(" -d, --debug - causes corerun to wait for a debugger to attach before executing\n") + W(" -?, -h, --help - show this help\n") + W("\n") + W("CoreCLR is searched for in %%CORE_ROOT%%, then in the directory\n") + W("the corerun binary is located.\n")); } -bool TryRun(const int argc, const wchar_t* argv[], Logger &log, const bool verbose, const bool waitForDebugger, DWORD &exitCode) +// Parse the command line arguments +static bool parse_args( + const int argc, + const char_t* argv[], + configuration& config) { - // 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) + // The command line must contain at least the current exe name and the managed assembly path. + if (argc < 2) { - log << W("No exename specified.") << Logger::endl; + display_usage(); return false; } - StackSString appPath; - StackSString appNiPath; - StackSString managedAssemblyFullName; - - 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 || filePart == NULL) + for (int i = 1; i < argc; i++) { - log << W("Failed to get full path: ") << exeName << Logger::endl; - log << W("Error code: ") << GetLastError() << Logger::endl; - return false; - } - - managedAssemblyFullName.Set(appPathPtr); + bool is_option = pal::is_cli_option(argv[i][0]); - *(filePart) = W('\0'); - appPath.CloseBuffer(DWORD(filePart - appPathPtr)); - - log << W("Loading: ") << managedAssemblyFullName.GetUnicode() << Logger::endl; + // First argument that is not an option is the managed assembly to execute. + if (!is_option) + { + config.entry_assembly_fullpath = pal::get_absolute_path(argv[i]); + i++; // Move to next argument. - appNiPath.Set(appPath); - appNiPath.Append(W("NI")); - appNiPath.Append(W(";")); - appNiPath.Append(appPath); + config.entry_assembly_argc = argc - i; + config.entry_assembly_argv = (const char_t**)::malloc(config.entry_assembly_argc * sizeof(const char_t*)); + assert(config.entry_assembly_argv != nullptr); + for (int c = 0; c < config.entry_assembly_argc; ++c) + { + config.entry_assembly_argv[c] = pal::strdup(argv[i + c]); + } - // 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); + // Successfully parsed arguments. + return true; + } - // Preload mock hostpolicy if requested. - StackSString hostpolicyPath; - if (WszGetEnvironmentVariable(W("MOCK_HOSTPOLICY"), hostpolicyPath) > 0 && hostpolicyPath.GetCount() > 0) - { - if (!TryLoadHostPolicy(hostpolicyPath)) + const char_t* arg = argv[i]; + size_t arg_len = pal::strlen(arg); + if (arg_len == 1) { - log << W("Unable to load requested mock hostpolicy."); - return false; + pal::fprintf(stderr, W("Option %s: invalid form\n"), arg); + break; // Invalid option } - } - - if (!hostEnvironment.IsHostLoaded()) - { - return false; - } - HRESULT hr = E_FAIL; - void* hostHandle = nullptr; - DWORD domainId = 0; + const char_t* option = arg + 1; + if (option[0] == W('-')) // Handle double '--' + option++; - hr = InitializeHost(log, hostEnvironment, appPath, appNiPath, nativeDllSearchDirs, &hostHandle, (unsigned int*)&domainId); - if (FAILED(hr)) - { - exitCode = hr; - return false; - } - - if(waitForDebugger) - { - if(!IsDebuggerPresent()) + // Path to core_root + if (pal::strcmp(option, W("c")) == 0 || (pal::strcmp(option, W("clr-path")) == 0)) { - log << W("Waiting for the debugger to attach. Press any key to continue ...") << Logger::endl; - getchar(); - if (IsDebuggerPresent()) + i++; + if (i < argc) { - log << "Debugger is attached." << Logger::endl; + config.clr_path = argv[i]; } else { - log << "Debugger failed to attach." << Logger::endl; + pal::fprintf(stderr, W("Option %s: missing path\n"), arg); + break; } } + else if ((pal::strcmp(option, W("d")) == 0 || (pal::strcmp(option, W("debug")) == 0))) + { + config.wait_to_debug = true; + } + else if ((pal::strcmp(option, W("v")) == 0 || (pal::strcmp(option, W("verbose")) == 0))) + { + config.verbose = true; + } + else if (pal::strcmp(option, W("st")) == 0) + { + config.self_test = true; + return true; + } + else if ((pal::strcmp(option, W("?")) == 0 || (pal::strcmp(option, W("h")) == 0 || (pal::strcmp(option, W("help")) == 0)))) + { + display_usage(); + break; + } + else + { + pal::fprintf(stderr, W("Unknown option %s\n"), arg); + break; + } } - hr = ExecuteAssembly(log, hostEnvironment, managedAssemblyFullName, hostHandle, domainId, argc - 1, (argc - 1) ? &(argv[1]) : NULL, (unsigned int*)&exitCode); - if (FAILED(hr)) - { - return false; - } + return false; +} - log << W("App exit value = ") << exitCode << Logger::endl; +static int self_test(); - hr = ShutdownHost(log, hostEnvironment, hostHandle, domainId, (unsigned int*)&exitCode); - if (FAILED(hr)) - { - return false; - } +// +// Entry points +// - return true; -} +int MAIN(const int argc, const char_t* argv[]) +{ + configuration config{}; + if (!parse_args(argc, argv, config)) + return EXIT_FAILURE; -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 - ); + if (config.self_test) + return self_test(); + + int exit_code = run(config); + return exit_code; } -int __cdecl wmain(const int argc, const wchar_t* argv[]) +#ifdef TARGET_WINDOWS +// Used by CoreShim to determine running CoreCLR details. +extern "C" __declspec(dllexport) HRESULT __cdecl GetCurrentClrDetails(void** clrInstance, unsigned int* appDomainId) { - // 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; + assert(clrInstance != nullptr && appDomainId != nullptr); + *clrInstance = CurrentClrInstance; + *appDomainId = CurrentAppDomainId; + return S_OK; +} +#endif // TARGET_WINDOWS - auto stringsEqual = [](const wchar_t * const a, const wchar_t * const b) -> bool { - return ::_wcsicmp(a, b) == 0; - }; +// +// Tests +// - 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; +#define THROW_IF_FALSE(stmt) if (!(stmt)) throw W(#stmt); +static int self_test() +{ + try + { + { + configuration config{}; + const char_t* args[] = { W(""), W("-d"), W("-v"), W("foo") }; + THROW_IF_FALSE(parse_args(4, args, config)); + THROW_IF_FALSE(config.wait_to_debug); + THROW_IF_FALSE(config.verbose); + THROW_IF_FALSE(config.clr_path.empty()); + THROW_IF_FALSE(!config.entry_assembly_fullpath.empty()); + THROW_IF_FALSE(config.entry_assembly_argc == 0); } - }; - - 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(); + { + configuration config{}; + const char_t* args[] = { W(""), W("-d"), W("foo"), W("1"), W("2"), W("3") }; + THROW_IF_FALSE(parse_args(6, args, config)); + THROW_IF_FALSE(!config.verbose); + THROW_IF_FALSE(config.wait_to_debug); + THROW_IF_FALSE(!config.entry_assembly_fullpath.empty()); + THROW_IF_FALSE(config.entry_assembly_argc == 3); } - - DWORD exitCode; - auto success = TryRun(newArgc, newArgv, log, verbose, waitForDebugger, exitCode); - - log << W("Execution ") << (success ? W("succeeded") : W("failed")) << Logger::endl; - - return exitCode; + { + configuration config{}; + const char_t* args[] = { W(""), W("--clr-path"), W("path"), W("foo"), W("1") }; + THROW_IF_FALSE(parse_args(5, args, config)); + THROW_IF_FALSE(!config.verbose); + THROW_IF_FALSE(!config.wait_to_debug); + THROW_IF_FALSE(config.clr_path == W("path")); + THROW_IF_FALSE(!config.entry_assembly_fullpath.empty()); + THROW_IF_FALSE(config.entry_assembly_argc == 1); + } + { + string_t path; + path = W("path"); + pal::ensure_trailing_delimiter(path); + THROW_IF_FALSE(path.back() == pal::dir_delim); + path = W(""); + pal::ensure_trailing_delimiter(path); + THROW_IF_FALSE(path.back() == pal::dir_delim); + path = W("\\"); + pal::ensure_trailing_delimiter(path); + THROW_IF_FALSE(path.back() == pal::dir_delim || path.length() == 1); + path = W("/"); + pal::ensure_trailing_delimiter(path); + THROW_IF_FALSE(path.back() == pal::dir_delim || path.length() == 1); + } + { + THROW_IF_FALSE(!pal::string_ends_with(W(""), W(".cd"))); + THROW_IF_FALSE(pal::string_ends_with(W("ab.cd"), W(".cd"))); + THROW_IF_FALSE(!pal::string_ends_with(W("ab.cd"), W(".cde"))); + THROW_IF_FALSE(!pal::string_ends_with(W("ab.cd"), W("ab.cde"))); + } + } + catch (const char_t msg[]) + { + pal::fprintf(stderr, W("Fail: %s\n"), msg); + return EXIT_FAILURE; } + + pal::fprintf(stdout, W("Self-test passed.\n")); + return EXIT_SUCCESS; } diff --git a/src/coreclr/hosts/corerun/corerun.hpp b/src/coreclr/hosts/corerun/corerun.hpp new file mode 100644 index 0000000000000..0dc40d737ff4f --- /dev/null +++ b/src/coreclr/hosts/corerun/corerun.hpp @@ -0,0 +1,617 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __CORERUN_HPP__ +#define __CORERUN_HPP__ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// Class used to perform specific actions. +class platform_specific_actions; + +// +// Platform abstraction layer +// + +namespace pal +{ + // Handle to a loaded module + using mod_t = void*; + + struct free_delete + { + void operator()(void* x) { ::free(x); } + }; + + template + using malloc_ptr = std::unique_ptr; +} + +#ifdef TARGET_WINDOWS +#include + +#define MAIN __cdecl wmain +#define W(str) L ## str + +namespace pal +{ + using char_t = wchar_t; + using string_t = std::basic_string; + using stringstream_t = std::basic_stringstream; + using string_utf8_t = std::basic_string; + + const char_t dir_delim = W('\\'); + const char_t env_path_delim = W(';'); + const char_t nativelib_ext[] = W(".dll"); + const char_t coreclr_lib[] = W("coreclr"); + + int strcmp(const char_t* str1, const char_t* str2) { return wcscmp(str1, str2); } + size_t strlen(const char_t* str) { return wcslen(str); } + char_t* strdup(const char_t* str) { return ::_wcsdup(str); } + int fprintf(FILE* fd, const char_t* const fmt, ...) + { + va_list args; + va_start(args, fmt); + int ret = ::vfwprintf(fd, fmt, args); + va_end(args); + return ret; + } + bool is_cli_option(const char_t option_maybe) { return option_maybe == W('-') || option_maybe == W('/'); } + string_t getenv(const char_t* var) + { + DWORD needed = ::GetEnvironmentVariableW(var, nullptr, 0); + if (needed == 0) + return {}; + + malloc_ptr buffer{ (char_t*)::malloc(needed * sizeof(char_t)) }; + assert(buffer != nullptr); + DWORD wrote = ::GetEnvironmentVariableW(var, buffer.get(), needed); + assert(wrote < needed); + return { buffer.get() }; + } + string_t get_exe_path() + { + char_t file_name[1024]; + DWORD count = ::GetModuleFileNameW(nullptr, file_name, ARRAYSIZE(file_name)); + assert(::GetLastError() != ERROR_INSUFFICIENT_BUFFER); + + return { file_name }; + } + string_t get_absolute_path(const char_t* path) + { + DWORD needed = ::GetFullPathNameW(path, 0, nullptr, nullptr); + malloc_ptr buffer{ (char_t*)::malloc(needed * sizeof(char_t)) }; + assert(buffer != nullptr); + + DWORD wrote = ::GetFullPathNameW(path, needed, buffer.get(), nullptr); + assert(wrote < needed); + return { buffer.get() }; + } + + bool is_debugger_attached() { return ::IsDebuggerPresent() == TRUE; } + + bool does_file_exist(const string_t& file_path) + { + return INVALID_FILE_ATTRIBUTES != ::GetFileAttributesW(file_path.c_str()); + } + + // Forward declaration + void ensure_trailing_delimiter(pal::string_t& dir); + + string_t build_file_list( + const string_t& dir, + const char_t* ext, + std::function should_add) + { + assert(ext != nullptr); + + string_t dir_local = dir; + dir_local.append(W("*")); + dir_local.append(ext); + + WIN32_FIND_DATA data; + HANDLE findHandle = ::FindFirstFileW(dir_local.data(), &data); + if (findHandle == INVALID_HANDLE_VALUE) + return {}; + + stringstream_t file_list; + do + { + if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + // ToLower for case-insensitive comparisons + char_t* fileNameChar = data.cFileName; + while (*fileNameChar) + { + *fileNameChar = towlower(*fileNameChar); + fileNameChar++; + } + + if (should_add(data.cFileName)) + file_list << dir << data.cFileName << env_path_delim; + } + } while (FALSE != ::FindNextFileW(findHandle, &data)); + + ::FindClose(findHandle); + + return file_list.str(); + } + + void* get_module_symbol(mod_t m, const char* sym) + { + assert(m != nullptr && sym != nullptr); + return ::GetProcAddress((HMODULE)m, sym); + } + + string_utf8_t convert_to_utf8(const char_t* str) + { + // Compute the needed buffer + int bytes_req = ::WideCharToMultiByte( + CP_UTF8, 0, // Conversion args + str, -1, // Input string + nullptr, 0, // Null to request side + nullptr, nullptr); + + malloc_ptr buffer{ (char*)::malloc(bytes_req) }; + assert(buffer != nullptr); + + int written = ::WideCharToMultiByte( + CP_UTF8, 0, // Conversion args + str, -1, // Input string + buffer.get(), bytes_req, // Output buffer + nullptr, nullptr); + assert(bytes_req == written); + + return { buffer.get() }; + } + + string_utf8_t convert_to_utf8(string_t&& str) + { + return convert_to_utf8(str.c_str()); + } + + bool try_load_hostpolicy(pal::string_t mock_hostpolicy_value) + { + const char_t* hostpolicyName = W("hostpolicy.dll"); + pal::mod_t hMod = (pal::mod_t)::GetModuleHandleW(hostpolicyName); + if (hMod != nullptr) + return true; + + // Check if a hostpolicy exists and if it does, load it. + if (pal::does_file_exist(mock_hostpolicy_value)) + hMod = (pal::mod_t)::LoadLibraryExW(mock_hostpolicy_value.c_str(), nullptr, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + + if (hMod == nullptr) + pal::fprintf(stderr, W("Failed to load mock hostpolicy at path '%s'. Error: 0x%08x\n"), mock_hostpolicy_value.c_str(), ::GetLastError()); + + return hMod != nullptr; + } + + bool try_load_coreclr(const pal::string_t& core_root, pal::mod_t& hMod) + { + pal::string_t coreclr_path = core_root; + pal::ensure_trailing_delimiter(coreclr_path); + coreclr_path.append(pal::coreclr_lib); + coreclr_path.append(pal::nativelib_ext); + + hMod = (pal::mod_t)::LoadLibraryExW(coreclr_path.c_str(), nullptr, 0); + if (hMod == nullptr) + { + pal::fprintf(stderr, W("Failed to load: '%s'. Error: 0x%08x\n"), coreclr_path.c_str(), ::GetLastError()); + return false; + } + + // Pin the module - CoreCLR.dll does not support being unloaded. + HMODULE unused; + if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, coreclr_path.c_str(), &unused)) + { + pal::fprintf(stderr, W("Failed to pin: '%s'. Error: 0x%08x\n"), coreclr_path.c_str(), ::GetLastError()); + return false; + } + + return true; + } +} + +class platform_specific_actions final +{ + HANDLE _actCxt; + ULONG_PTR _actCookie; + +public: + platform_specific_actions() = default; + + void before_coreclr_load() { } + + void before_execute_assembly(const pal::string_t& assembly) + { + ACTCTX cxt{}; + cxt.cbSize = sizeof(cxt); + cxt.dwFlags = (ACTCTX_FLAG_APPLICATION_NAME_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID); + cxt.lpSource = assembly.c_str(); + cxt.lpResourceName = MAKEINTRESOURCEW(1); // The CreateProcess manifest which contains the context details + + _actCxt = ::CreateActCtxW(&cxt); + if (_actCxt == INVALID_HANDLE_VALUE) + { + _actCookie = ULONG_PTR{}; + DWORD err = ::GetLastError(); + if (err != ERROR_RESOURCE_TYPE_NOT_FOUND && err != ERROR_RESOURCE_DATA_NOT_FOUND) + pal::fprintf(stderr, W("Activation Context creation failed. Error: 0x%08x\n"), err); + } + else + { + BOOL res = ::ActivateActCtx(_actCxt, &_actCookie); + if (res == FALSE) + pal::fprintf(stderr, W("Failed to activate Activation Context. Error: 0x%08x\n"), ::GetLastError()); + } + } + + void after_execute_assembly() + { + if (_actCookie != ULONG_PTR{}) + { + BOOL res = ::DeactivateActCtx(0, _actCookie); + if (res == FALSE) + pal::fprintf(stderr, W("Failed to de-activate Activation Context. Error: 0x%08x\n"), ::GetLastError()); + } + + if (_actCxt != INVALID_HANDLE_VALUE) + ::ReleaseActCtx(_actCxt); + } +}; + +#else // !TARGET_WINDOWS +#include +#include +#include +#include +#include + +#if defined(__APPLE__) +// Needed for detecting the debugger attach scenario +#include +#include +#endif // __APPLE__ + +// CMake generated +#include +#include + +#define MAIN main +#define W(str) str +#define FAILED(result) (result < 0) + +#if !HAVE_DIRENT_D_TYPE +#define DT_UNKNOWN 0 +#define DT_DIR 4 +#define DT_REG 8 +#define DT_LNK 10 +#endif + +namespace pal +{ + using char_t = char; + using string_t = std::basic_string; + using stringstream_t = std::basic_stringstream; + using string_utf8_t = std::basic_string; + + const char_t dir_delim = W('/'); + const char_t env_path_delim = W(':'); + +#if defined(__APPLE__) + const char_t nativelib_ext[] = W(".dylib"); +#else // Various Linux-related OS-es + const char_t nativelib_ext[] = W(".so"); +#endif + const char_t coreclr_lib[] = W("libcoreclr"); + + int strcmp(const char_t* str1, const char_t* str2) { return ::strcmp(str1, str2); } + size_t strlen(const char_t* str) { return ::strlen(str); } + char_t* strdup(const char_t* str) { return ::strdup(str); } + int fprintf(FILE* fd, const char_t* const fmt, ...) + { + va_list args; + va_start(args, fmt); + int ret = ::vfprintf(fd, fmt, args); + va_end(args); + return ret; + } + bool is_cli_option(const char_t option_maybe) { return option_maybe == W('-'); } + string_t getenv(const char_t* var) + { + const char_t* val = ::getenv(var); + if (val == nullptr) + return {}; + return { val }; + } + + string_t get_exe_path() { return { getexepath() }; } + + string_t get_absolute_path(const string_t& path) + { + string_t abs_path = path; + + char_t realPath[PATH_MAX]; + if (realpath(path.c_str(), realPath) != nullptr && realPath[0] != W('\0')) + { + abs_path.assign(realPath); + // realpath should return canonicalized path without the trailing slash + assert(abs_path.back() != W('/')); + } + + return abs_path; + } + + bool is_debugger_attached() + { +#if defined(__APPLE__) + // Taken from https://developer.apple.com/library/archive/qa/qa1361/_index.html + int junk; + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + assert(junk == 0); + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); +#else // !__APPLE__ + pal::fprintf(stdout, W("Debugger attach is not supported on this platform\n")); + return true; +#endif // !__APPLE__ + } + + bool does_file_exist(const char_t* file_path) + { + // Check if the specified path exists + struct stat sb; + if (stat(file_path, &sb) == -1) + { + perror(W("Path not found")); + return false; + } + + // Verify that the path points to a file + if (!S_ISREG(sb.st_mode)) + { + pal::fprintf(stderr, W("The specified managed assembly is not a file: %s\n"), file_path); + return false; + } + + return true; + } + + // Forward declaration + bool string_ends_with(const string_t& str, size_t suffix_len, const char_t* suffix); + void ensure_trailing_delimiter(pal::string_t& dir); + + string_t build_file_list( + const string_t& directory, + const char_t* ext, + std::function should_add) + { + assert(ext != nullptr); + const size_t ext_len = pal::strlen(ext); + + DIR* dir = opendir(directory.c_str()); + if (dir == nullptr) + return {}; + + stringstream_t file_list; + + // For all entries in the directory + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) + { +#if HAVE_DIRENT_D_TYPE + int dirEntryType = entry->d_type; +#else + int dirEntryType = DT_UNKNOWN; +#endif + + // We are interested in files only + switch (dirEntryType) + { + case DT_REG: + break; + + // Handle symlinks and file systems that do not support d_type + case DT_LNK: + case DT_UNKNOWN: + { + string_t full_filename; + full_filename.append(directory); + full_filename.append(1, pal::dir_delim); + full_filename.append(entry->d_name); + if (!does_file_exist(full_filename.c_str())) + continue; + } + break; + + default: + continue; + } + + // Check if the extension matches the one we are looking for + if (!string_ends_with(entry->d_name, ext_len, ext)) + continue; + + // Make sure if we have an assembly with multiple extensions present, + // we insert only one version of it. + if (should_add(entry->d_name)) + file_list << directory << dir_delim << entry->d_name << env_path_delim; + } + + closedir(dir); + return file_list.str(); + } + + void* get_module_symbol(mod_t m, const char* sym) + { + assert(m != nullptr && sym != nullptr); + return dlsym(m, sym); + } + + string_utf8_t convert_to_utf8(const char_t* str) + { + return { str }; + } + + string_utf8_t convert_to_utf8(string_t&& str) + { + return std::move(str); + } + + bool try_load_hostpolicy(pal::string_t mock_hostpolicy_value) + { + if (!string_ends_with(mock_hostpolicy_value, pal::nativelib_ext)) + mock_hostpolicy_value.append(pal::nativelib_ext); + + pal::mod_t hMod = (pal::mod_t)dlopen(mock_hostpolicy_value.c_str(), RTLD_LAZY); + if (hMod == nullptr) + pal::fprintf(stderr, W("Failed to load mock hostpolicy at path '%s'. Error: %s\n"), mock_hostpolicy_value.c_str(), dlerror()); + + return hMod != nullptr; + } + + bool try_load_coreclr(const pal::string_t& core_root, pal::mod_t& hMod) + { + pal::string_t coreclr_path = core_root; + pal::ensure_trailing_delimiter(coreclr_path); + coreclr_path.append(pal::coreclr_lib); + coreclr_path.append(pal::nativelib_ext); + + hMod = (pal::mod_t)dlopen(coreclr_path.c_str(), RTLD_NOW | RTLD_LOCAL); + if (hMod == nullptr) + { + pal::fprintf(stderr, W("Failed to load: '%s'. Error: %s\n"), coreclr_path.c_str(), dlerror()); + return false; + } + + return true; + } +} + +class platform_specific_actions final +{ +public: + platform_specific_actions() = default; + + void before_coreclr_load() + { +#ifdef HOST_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/runtime/issues/6479 + // + // 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("UNW_ARM_UNWIND_METHOD=6")); +#endif // HOST_ARM + } + + void before_execute_assembly(const pal::string_t& assembly) { } + + void after_execute_assembly() { } +}; + +#endif // !TARGET_WINDOWS + +namespace pal +{ + void split_path_to_dir_filename(const pal::string_t& path, pal::string_t& dir, pal::string_t& filename) + { + size_t pos = path.find_last_of(dir_delim); + if (pos == pal::string_t::npos) + { + dir = {}; + filename = path; + return; + } + + dir = path.substr(0, pos); + filename = path.substr(pos + 1); + } + + bool string_ends_with(const string_t& str, size_t suffix_len, const char_t* suffix) + { + assert(suffix != nullptr); + + size_t str_len = str.length(); + if (str_len < suffix_len) + return false; + + const char_t* suffix_maybe = str.data() + (str_len - suffix_len); + return ::memcmp(suffix_maybe, suffix, suffix_len * sizeof(char_t)) == 0; + } + + template + bool string_ends_with(const string_t& str, const char_t(&suffix)[LEN]) + { + return string_ends_with(str, LEN - 1, suffix); + } + + void ensure_trailing_delimiter(pal::string_t& dir) + { + if (dir.empty()) + { + dir = pal::dir_delim; + } + else if (dir.back() != pal::dir_delim) + { + dir.push_back(pal::dir_delim); + } + } + + const char** convert_argv_to_utf8(int argc, const char_t** argv, std::vector& lifetime) + { + malloc_ptr ret{ (const char**)::malloc(sizeof(char*) * argc) }; + assert(ret != nullptr); + + lifetime.resize(argc); + for (int i = 0; i < argc; ++i) + { + string_utf8_t s = convert_to_utf8(argv[i]); + lifetime[i] = std::move(s); + ret.get()[i] = lifetime[i].c_str(); + } + + return ret.release(); + } +} + +#endif // __CORERUN_HPP__ diff --git a/src/coreclr/hosts/corerun/logger.cpp b/src/coreclr/hosts/corerun/logger.cpp deleted file mode 100644 index f3fd0477f1e67..0000000000000 --- a/src/coreclr/hosts/corerun/logger.cpp +++ /dev/null @@ -1,282 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - - - -#include -#include -#include -#include "palclr.h" -#include "sstring.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 0x80131419: 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<< (const char *val) { - if (m_isEnabled) { - EnsurePrefixIsPrinted(); - - SString valUTF8(SString::Utf8Literal, val); - SmallStackSString valUnicode; - valUTF8.ConvertToUnicode(valUnicode); - - print(valUnicode); - } - 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 deleted file mode 100644 index fe954f806303f..0000000000000 --- a/src/coreclr/hosts/corerun/logger.h +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - - -// -// 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<< (const char* 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/unixcorerun/CMakeLists.txt b/src/coreclr/hosts/unixcorerun/CMakeLists.txt deleted file mode 100644 index 9e768f97707d3..0000000000000 --- a/src/coreclr/hosts/unixcorerun/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -project(unixcorerun) - -include_directories("${CLR_SRC_NATIVE_DIR}/common") -include(configure.cmake) - -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -_add_executable(corerun corerun.cpp) - -target_link_libraries(corerun ${CMAKE_DL_LIBS}) - -# Android implements pthread natively -if(NOT CLR_CMAKE_TARGET_ANDROID) - target_link_libraries(corerun pthread) -endif() - -install_clr(TARGETS corerun) diff --git a/src/coreclr/hosts/unixcorerun/corerun.cpp b/src/coreclr/hosts/unixcorerun/corerun.cpp deleted file mode 100644 index 8e00aeac409fb..0000000000000 --- a/src/coreclr/hosts/unixcorerun/corerun.cpp +++ /dev/null @@ -1,581 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "coreclrhost.h" -#include -#ifndef SUCCEEDED -#define SUCCEEDED(Status) ((Status) >= 0) -#endif // !SUCCEEDED - -#include -#include - -#if !HAVE_DIRENT_D_TYPE -#define DT_UNKNOWN 0 -#define DT_DIR 4 -#define DT_REG 8 -#define DT_LNK 10 -#endif - -// 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 = "COMPlus_gcServer"; - -// Name of environment variable to control "System.Globalization.Invariant" -// Set to 1 for Globalization Invariant mode to be true. Default is false. -static const char* globalizationInvariantVar = "CORECLR_GLOBAL_INVARIANT"; - -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 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 (size_t 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) - { -#if HAVE_DIRENT_D_TYPE - int dirEntryType = entry->d_type; -#else - int dirEntryType = DT_UNKNOWN; -#endif - - // We are interested in files only - switch (dirEntryType) - { - 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); -} - -const char* GetEnvValueBoolean(const char* envVariable) -{ - const char* envValue = std::getenv(envVariable); - if (envValue == nullptr) - { - envValue = "0"; - } - // CoreCLR expects strings "true" and "false" instead of "1" and "0". - return (std::strcmp(envValue, "1") == 0 || strcasecmp(envValue, "true") == 0) ? "true" : "false"; -} - -static void *TryLoadHostPolicy(const char *hostPolicyPath) -{ -#if defined(__APPLE__) - static const char LibrarySuffix[] = ".dylib"; -#else // Various Linux-related OS-es - static const char LibrarySuffix[] = ".so"; -#endif - - std::string hostPolicyCompletePath(hostPolicyPath); - hostPolicyCompletePath.append(LibrarySuffix); - - void *libraryPtr = dlopen(hostPolicyCompletePath.c_str(), RTLD_LAZY); - if (libraryPtr == nullptr) - { - fprintf(stderr, "Failed to load mock hostpolicy at path '%s'. Error: %s", hostPolicyCompletePath.c_str(), dlerror()); - } - - return libraryPtr; -} - -int ExecuteManagedAssembly( - const char* currentExeAbsolutePath, - const char* clrFilesAbsolutePath, - const char* managedAssemblyAbsolutePath, - int managedAssemblyArgc, - const char** managedAssemblyArgv) -{ - // Indicates failure - int exitCode = -1; - -#ifdef HOST_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/runtime/issues/6479 - // - // 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("UNW_ARM_UNWIND_METHOD=6")); -#endif // HOST_ARM - -#if defined(__APPLE__) - static const char* const coreClrDll = "libcoreclr.dylib"; -#else - static const char* const coreClrDll = "libcoreclr.so"; -#endif - - 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); - - std::string tpaList; - // Construct native search directory paths - std::string nativeDllSearchDirs(appPath); - char *coreLibraries = getenv("CORE_LIBRARIES"); - if (coreLibraries) - { - nativeDllSearchDirs.append(":"); - nativeDllSearchDirs.append(coreLibraries); - if (std::strcmp(coreLibraries, clrFilesAbsolutePath) != 0) - { - AddFilesFromDirectoryToTpaList(coreLibraries, tpaList); - } - } - nativeDllSearchDirs.append(":"); - nativeDllSearchDirs.append(clrFilesAbsolutePath); - - void* hostpolicyLib = nullptr; - char* mockHostpolicyPath = getenv("MOCK_HOSTPOLICY"); - if (mockHostpolicyPath) - { - hostpolicyLib = TryLoadHostPolicy(mockHostpolicyPath); - if (hostpolicyLib == nullptr) - { - return -1; - } - } - - 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_2_ptr shutdownCoreCLR = (coreclr_shutdown_2_ptr)dlsym(coreclrLib, "coreclr_shutdown_2"); - - 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_2 not found in the libcoreclr.so\n"); - } - else - { - // Check whether we are enabling server GC (off by default) - const char* useServerGc = GetEnvValueBoolean(serverGcVar); - - // Check Globalization Invariant mode (false by default) - const char* globalizationInvariant = GetEnvValueBoolean(globalizationInvariantVar); - - // 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", - "System.GC.Server", - "System.Globalization.Invariant", - }; - 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(), - // System.GC.Server - useServerGc, - // System.Globalization.Invariant - globalizationInvariant, - }; - - 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; - } - - int latchedExitCode = 0; - st = shutdownCoreCLR(hostHandle, domainId, &latchedExitCode); - if (!SUCCEEDED(st)) - { - fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st); - exitCode = -1; - } - - if (exitCode != -1) - { - exitCode = latchedExitCode; - } - } - } - } - else - { - const char* error = dlerror(); - fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error); - } - - if (hostpolicyLib) - { - if(dlclose(hostpolicyLib) != 0) - { - fprintf(stderr, "Warning - dlclose of mock hostpolicy failed.\n"); - } - } - - return exitCode; -} - -// 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 main(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]. - char* path = getexepath(); - if (!path) - { - perror("Could not get full path"); - return -1; - } - - std::string argv0AbsolutePath(path); - free(path); - - 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; -} From 6f6a9dad37ad76c1555b9e809e633bcb78e71ff3 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sat, 13 Mar 2021 16:10:38 -0800 Subject: [PATCH 3/9] Missed forward declare of overload. --- src/coreclr/hosts/corerun/corerun.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/hosts/corerun/corerun.hpp b/src/coreclr/hosts/corerun/corerun.hpp index 0dc40d737ff4f..47fcc5735a396 100644 --- a/src/coreclr/hosts/corerun/corerun.hpp +++ b/src/coreclr/hosts/corerun/corerun.hpp @@ -412,6 +412,8 @@ namespace pal } // Forward declaration + template + bool string_ends_with(const string_t& str, const char_t(&suffix)[LEN]); bool string_ends_with(const string_t& str, size_t suffix_len, const char_t* suffix); void ensure_trailing_delimiter(pal::string_t& dir); From 05faa28b4a1a01fe9025877c5a2c3d6a67ece745 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Sat, 13 Mar 2021 18:37:04 -0800 Subject: [PATCH 4/9] Dump the computed configuration for runtime if a failure to initialize or execute the entry assembly occurs. Remove -v flag. --- src/coreclr/hosts/corerun/corerun.cpp | 75 +++++++++++++++++++++------ 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index cd3b50f0b537b..1d8e432ada09a 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -43,9 +43,6 @@ struct configuration // Wait for debugger to be attached. bool wait_to_debug; - // Verbose output from corerun. - bool verbose; - // Perform self test. bool self_test; }; @@ -166,6 +163,49 @@ static const char* get_envvar_as_boolean(const char_t* var, bool def = false) : "false"; } +class logger_t final +{ + const char* _exePath; + int _propertyCount; + const char** _propertyKeys; + const char** _propertyValues; + const char* _managedAssembly; + int _argc; + const char** _argv; +public: + logger_t( + const char* exePath, + int propertyCount, const char** propertyKeys, const char** propertyValues, + const char* managedAssembly, int argc, const char** argv) + : _exePath{ exePath } + , _propertyCount{ propertyCount } + , _propertyKeys{ propertyKeys } + , _propertyValues{ propertyValues } + , _managedAssembly{ managedAssembly } + , _argc{ argc } + , _argv{ argv } + { } + + void dump_details(FILE* fd = stderr) + { + // Using std::fprintf since values have been converted to UTF-8. + std::fprintf(fd, "Exe path: %s\n", _exePath); + std::fprintf(fd, "Properties:\n"); + for (int i = 0; i < _propertyCount; ++i) + { + std::fprintf(fd, " %s = %s\n", _propertyKeys[i], _propertyValues[i]); + } + + std::fprintf(fd, "Managed assembly: %s\n", _managedAssembly); + std::fprintf(fd, "Arguments (%d): ", _argc); + for (int i = 0; i < _argc; ++i) + { + std::fprintf(fd, "%s ", _argv[i]); + } + std::fprintf(fd, "\n"); + } +}; + // The current CoreCLR instance details. static void* CurrentClrInstance; static unsigned int CurrentAppDomainId; @@ -310,24 +350,33 @@ static int run(const configuration& config) enable_globalization_invariant, }; + int propertyCount = (int)(sizeof(propertyKeys) / sizeof(propertyKeys[0])); + // Construct arguments pal::string_utf8_t exe_path_utf8 = pal::convert_to_utf8(std::move(exe_path)); std::vector argv_lifetime; pal::malloc_ptr argv_utf8{ pal::convert_argv_to_utf8(config.entry_assembly_argc, config.entry_assembly_argv, argv_lifetime) }; pal::string_utf8_t entry_assembly_utf8 = pal::convert_to_utf8(config.entry_assembly_fullpath.c_str()); + logger_t logger{ + exe_path_utf8.c_str(), + propertyCount, propertyKeys, propertyValues, + entry_assembly_utf8.c_str(), config.entry_assembly_argc, argv_utf8.get() }; + int result; result = coreclr_init_func( exe_path_utf8.c_str(), "corerun", - sizeof(propertyKeys) / sizeof(propertyKeys[0]), + propertyCount, propertyKeys, propertyValues, &CurrentClrInstance, &CurrentAppDomainId); if (FAILED(result)) { - pal::fprintf(stderr, W("coreclr_initialize failed - Error: 0x%08x\n"), result); + pal::fprintf(stderr, W("BEGIN: coreclr_initialize failed - Error: 0x%08x\n"), result); + logger.dump_details(); + pal::fprintf(stderr, W("END: coreclr_initialize failed - Error: 0x%08x\n"), result); return -1; } @@ -344,7 +393,9 @@ static int run(const configuration& config) (uint32_t*)&exit_code); if (FAILED(result)) { - pal::fprintf(stderr, W("coreclr_execute_assembly failed - Error: 0x%08x\n"), result); + pal::fprintf(stderr, W("BEGIN: coreclr_execute_assembly failed - Error: 0x%08x\n"), result); + logger.dump_details(); + pal::fprintf(stderr, W("END: coreclr_execute_assembly failed - Error: 0x%08x\n"), result); return -1; } @@ -376,7 +427,6 @@ static void display_usage() W("\n") W("Options:\n") W(" -c, --clr-path - path to CoreCLR binary and managed CLR assemblies\n") - W(" -v, --verbose - causes verbose output to be written to the console\n") W(" -d, --debug - causes corerun to wait for a debugger to attach before executing\n") W(" -?, -h, --help - show this help\n") W("\n") @@ -449,10 +499,6 @@ static bool parse_args( { config.wait_to_debug = true; } - else if ((pal::strcmp(option, W("v")) == 0 || (pal::strcmp(option, W("verbose")) == 0))) - { - config.verbose = true; - } else if (pal::strcmp(option, W("st")) == 0) { config.self_test = true; @@ -514,10 +560,9 @@ static int self_test() { { configuration config{}; - const char_t* args[] = { W(""), W("-d"), W("-v"), W("foo") }; - THROW_IF_FALSE(parse_args(4, args, config)); + const char_t* args[] = { W(""), W("-d"), W("foo") }; + THROW_IF_FALSE(parse_args(3, args, config)); THROW_IF_FALSE(config.wait_to_debug); - THROW_IF_FALSE(config.verbose); THROW_IF_FALSE(config.clr_path.empty()); THROW_IF_FALSE(!config.entry_assembly_fullpath.empty()); THROW_IF_FALSE(config.entry_assembly_argc == 0); @@ -526,7 +571,6 @@ static int self_test() configuration config{}; const char_t* args[] = { W(""), W("-d"), W("foo"), W("1"), W("2"), W("3") }; THROW_IF_FALSE(parse_args(6, args, config)); - THROW_IF_FALSE(!config.verbose); THROW_IF_FALSE(config.wait_to_debug); THROW_IF_FALSE(!config.entry_assembly_fullpath.empty()); THROW_IF_FALSE(config.entry_assembly_argc == 3); @@ -535,7 +579,6 @@ static int self_test() configuration config{}; const char_t* args[] = { W(""), W("--clr-path"), W("path"), W("foo"), W("1") }; THROW_IF_FALSE(parse_args(5, args, config)); - THROW_IF_FALSE(!config.verbose); THROW_IF_FALSE(!config.wait_to_debug); THROW_IF_FALSE(config.clr_path == W("path")); THROW_IF_FALSE(!config.entry_assembly_fullpath.empty()); From cb759b239f98eeb2bc94d804ac988e4f16a8b73f Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Sat, 13 Mar 2021 20:43:24 -0800 Subject: [PATCH 5/9] Add debugger attach support for systems with procfs. --- src/coreclr/hosts/corerun/corerun.cpp | 26 +++++++---- src/coreclr/hosts/corerun/corerun.hpp | 65 ++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index 1d8e432ada09a..df517c5a3f987 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -74,18 +74,26 @@ namespace envvar static void wait_for_debugger() { - if (!pal::is_debugger_attached()) + pal::debugger_state_t state = pal::is_debugger_attached(); + if (state == pal::debugger_state_t::na) + { + pal::fprintf(stdout, W("Debugger attach is not available on this platform\n")); + return; + } + else if (state == pal::debugger_state_t::not_attached) { pal::fprintf(stdout, W("Waiting for the debugger to attach. Press any key to continue ...\n")); (void)getchar(); - if (pal::is_debugger_attached()) - { - pal::fprintf(stdout, W("Debugger is attached.\n")); - } - else - { - pal::fprintf(stdout, W("Debugger failed to attach.\n")); - } + state = pal::is_debugger_attached(); + } + + if (state == pal::debugger_state_t::attached) + { + pal::fprintf(stdout, W("Debugger is attached.\n")); + } + else + { + pal::fprintf(stdout, W("Debugger failed to attach.\n")); } } diff --git a/src/coreclr/hosts/corerun/corerun.hpp b/src/coreclr/hosts/corerun/corerun.hpp index 47fcc5735a396..af9cc1e80fb58 100644 --- a/src/coreclr/hosts/corerun/corerun.hpp +++ b/src/coreclr/hosts/corerun/corerun.hpp @@ -36,6 +36,13 @@ namespace pal template using malloc_ptr = std::unique_ptr; + + enum class debugger_state_t + { + na, + attached, + not_attached, + }; } #ifdef TARGET_WINDOWS @@ -99,7 +106,10 @@ namespace pal return { buffer.get() }; } - bool is_debugger_attached() { return ::IsDebuggerPresent() == TRUE; } + debugger_state_t is_debugger_attached() + { + return (::IsDebuggerPresent() == TRUE) ? debugger_state_t::attached : debugger_state_t::not_attached; + } bool does_file_exist(const string_t& file_path) { @@ -279,11 +289,14 @@ class platform_specific_actions final #include #include -#if defined(__APPLE__) // Needed for detecting the debugger attach scenario +#if defined(__APPLE__) #include #include -#endif // __APPLE__ +#else // !__APPLE__ +#include +#include +#endif // !__APPLE__ // CMake generated #include @@ -354,7 +367,7 @@ namespace pal return abs_path; } - bool is_debugger_attached() + debugger_state_t is_debugger_attached() { #if defined(__APPLE__) // Taken from https://developer.apple.com/library/archive/qa/qa1361/_index.html @@ -384,10 +397,48 @@ namespace pal // We're being debugged if the P_TRACED flag is set. - return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ) ? debugger_state_t::attached : debugger_state_t::not_attached; + #else // !__APPLE__ - pal::fprintf(stdout, W("Debugger attach is not supported on this platform\n")); - return true; + // Use procfs to detect if there is a tracer process. + // See https://www.kernel.org/doc/html/latest/filesystems/proc.html + char status[2048] = { 0 }; + int fd = ::open("/proc/self/status", O_RDONLY); + if (fd == -1) + { + // If the file can't be opened assume we are on a not supported platform. + return debugger_state_t::na; + } + + // Attempt to read + ssize_t bytes_read = ::read(fd, status, sizeof(status) - 1); + if (bytes_read > 0) + { + // We have data. At this point we can likely make a strong decision. + const char tracer_pid_name[] = "TracerPid:"; + const char* tracer_pid_ptr = ::strstr(status, tracer_pid_name); + if (tracer_pid_ptr == nullptr) + return debugger_state_t::not_attached; + + // The number after the name is the process ID of the + // tracer application or 0 if none exists. + const char* curr = tracer_pid_ptr + (sizeof(tracer_pid_name) - 1); + const char* end = status + bytes_read; + for (;curr < end; ++curr) + { + if (::isspace(*curr)) + continue; + + // Check the first non-space if it is 0. If so, we have + // a non-zero process ID and a tracer is attached. + return (::isdigit(*curr) && *curr != '0') ? debugger_state_t::attached : debugger_state_t::not_attached; + } + } + + // The read in data is either incomplete (i.e. small buffer) or + // the returned content is not expected. Let's fallback to not available. + return debugger_state_t::na; + #endif // !__APPLE__ } From 252153472c6e0874548608cb9cf57f3b5d27329e Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Sun, 14 Mar 2021 10:45:49 -0700 Subject: [PATCH 6/9] Simplfy CMake for including the coreclr/hosts project. --- src/coreclr/CMakeLists.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index 28ecbf7f16296..55a1b6cf207a3 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -80,7 +80,6 @@ if(CLR_CMAKE_HOST_UNIX) endif() add_subdirectory(pal) - add_subdirectory(hosts) else(CLR_CMAKE_HOST_UNIX) if(CLR_CMAKE_TARGET_UNIX) add_subdirectory(pal/src/libunwind) @@ -227,10 +226,7 @@ if (CLR_CMAKE_BUILD_SUBSET_RUNTIME) add_subdirectory(ildasm) add_subdirectory(ilasm) add_subdirectory(interop) - - if(CLR_CMAKE_HOST_WIN32) - add_subdirectory(hosts) - endif(CLR_CMAKE_HOST_WIN32) + add_subdirectory(hosts) else() if(CLR_CMAKE_HOST_UNIX) # this is needed to compile the jit on unix platforms. From 053cf2bb4661d93a8983c4ababa82441bdd6e29b Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Sun, 14 Mar 2021 11:07:17 -0700 Subject: [PATCH 7/9] Revert "Simplfy CMake for including the coreclr/hosts project." This reverts commit 51df3bf8e962624f4fb26f0e46cf412d4543a6da. --- src/coreclr/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index 55a1b6cf207a3..28ecbf7f16296 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -80,6 +80,7 @@ if(CLR_CMAKE_HOST_UNIX) endif() add_subdirectory(pal) + add_subdirectory(hosts) else(CLR_CMAKE_HOST_UNIX) if(CLR_CMAKE_TARGET_UNIX) add_subdirectory(pal/src/libunwind) @@ -226,7 +227,10 @@ if (CLR_CMAKE_BUILD_SUBSET_RUNTIME) add_subdirectory(ildasm) add_subdirectory(ilasm) add_subdirectory(interop) - add_subdirectory(hosts) + + if(CLR_CMAKE_HOST_WIN32) + add_subdirectory(hosts) + endif(CLR_CMAKE_HOST_WIN32) else() if(CLR_CMAKE_HOST_UNIX) # this is needed to compile the jit on unix platforms. From de2c6591618700d8f694537bc43423427c088fd2 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 15 Mar 2021 11:26:06 -0700 Subject: [PATCH 8/9] Add comments to the self-testing method. --- src/coreclr/hosts/corerun/corerun.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index df517c5a3f987..295d9c7ea0025 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -527,6 +527,7 @@ static bool parse_args( return false; } +// Forward declaration for self testing method. static int self_test(); // @@ -558,7 +559,7 @@ extern "C" __declspec(dllexport) HRESULT __cdecl GetCurrentClrDetails(void** clr #endif // TARGET_WINDOWS // -// Tests +// Self testing for corerun. // #define THROW_IF_FALSE(stmt) if (!(stmt)) throw W(#stmt); From c73af2e1a95aa425c6b890eeb1a8d99c15f8de42 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 16 Mar 2021 16:43:10 -0700 Subject: [PATCH 9/9] Remove the corerun option for setting System.Globalization.Invariant. --- src/coreclr/hosts/corerun/corerun.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index 295d9c7ea0025..ed2f059b15975 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -66,10 +66,6 @@ namespace envvar // Environment variable for setting whether or not to use Concurrent GC. // On by default. const char_t* concurrentGc = W("COMPlus_gcConcurrent"); - - // Name of environment variable to control "System.Globalization.Invariant" - // Set to 1 for Globalization Invariant mode to be true. Default is false. - const char_t* globalizationInvariant = W("CORECLR_GLOBAL_INVARIANT"); } static void wait_for_debugger() @@ -314,7 +310,6 @@ static int run(const configuration& config) pal::string_utf8_t native_search_dirs_utf8 = pal::convert_to_utf8(native_search_dirs.str()); const char* enable_server_gc = get_envvar_as_boolean(envvar::serverGc); const char* enable_concurrent_gc = get_envvar_as_boolean(envvar::concurrentGc, true /* default */); - const char* enable_globalization_invariant = get_envvar_as_boolean(envvar::globalizationInvariant); // Allowed property names: // @@ -337,7 +332,6 @@ static int run(const configuration& config) "NATIVE_DLL_SEARCH_DIRECTORIES", "System.GC.Server", "System.GC.Concurrent", - "System.Globalization.Invariant", }; const char* propertyValues[] = @@ -354,8 +348,6 @@ static int run(const configuration& config) enable_server_gc, // System.GC.Concurrent enable_concurrent_gc, - // System.Globalization.Invariant - enable_globalization_invariant, }; int propertyCount = (int)(sizeof(propertyKeys) / sizeof(propertyKeys[0]));