[netcore] Add target for running coreclr tests
authorMarek Safar <marek.safar@gmail.com>
Fri, 27 Sep 2019 08:19:58 +0000 (10:19 +0200)
committerMarek Safar <marek.safar@gmail.com>
Fri, 27 Sep 2019 19:27:08 +0000 (21:27 +0200)
Commit migrated from https://github.com/mono/mono/commit/f88da6cc14d6aa01048526e6f59f7cddafdce2cb

src/mono/Makefile.am
src/mono/configure.ac
src/mono/netcore/Makefile
src/mono/netcore/corerun/.gitignore [new file with mode: 0644]
src/mono/netcore/corerun/Makefile.am [new file with mode: 0644]
src/mono/netcore/corerun/coreclrhost.h [new file with mode: 0644]
src/mono/netcore/corerun/corerun.cpp [new file with mode: 0644]
src/mono/netcore/corerun/coreruncommon.cpp [new file with mode: 0644]
src/mono/netcore/corerun/coreruncommon.h [new file with mode: 0644]

index dc22a74..1211eff 100644 (file)
@@ -21,7 +21,7 @@ endif
 
 if ENABLE_NETCORE
 update_submodules = 
-SUBDIRS = mk llvm mono runtime netcore
+SUBDIRS = mk llvm mono runtime netcore netcore/corerun
 else
 update_submodules = update_submodules
 SUBDIRS = $(build_with_msvc) mk po $(libgc_dir) llvm mono $(ikvm_native_dir) support data runtime scripts man samples $(tools_dir) $(build_without_msvc) $(docs_dir) acceptance-tests
index 6f429b2..2f7f208 100644 (file)
@@ -6778,6 +6778,7 @@ tools/mono-hang-watchdog/Makefile
 runtime/Makefile
 msvc/Makefile
 po/Makefile
+netcore/corerun/Makefile
 ])
 
 if test x$host_win32 = xyes; then
index d141e06..54b433c 100644 (file)
@@ -6,6 +6,11 @@ NETCORETESTS_VERSION := $(shell cat ../eng/Versions.props | sed -n 's/.*Microsof
 NETCOREAPP_VERSION := $(shell cat ../eng/Versions.props | sed -n 's/.*MicrosoftNETCoreAppVersion>\(.*\)<\/MicrosoftNETCoreAppVersion.*/\1/p' )
 ROSLYN_VERSION:=  $(shell cat ../eng/Versions.props | sed -n 's/.*MicrosoftNetCompilersVersion>\(.*\)<\/MicrosoftNetCompilersVersion.*/\1/p' )
 
+# Should be automated but not sure how for now
+# the name is misleading osx version is used for any unix like systems
+# the url is manually extracted from coreclr-outerloop (https://dev.azure.com/dnceng/public/_build?definitionId=98&_a=summary) Build artifacts published page
+CORECLR_TESTS:=https://dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_apis/build/builds/368250/artifacts?artifactName=Tests_OSX_x64_checked_outerloop&api-version=5.2-preview.5&%24format=zip
+
 # runtime version used by $(DOTNET) - local .net core sdk to bootstrap stuff
 # it doesn't match NETCOREAPP_VERSION
 BOOTSTRAP_RUNTIME = $(shell ls ../.dotnet/shared/Microsoft.NETCore.App | tail -1)
@@ -234,6 +239,36 @@ build-test-corefx-%:
 xunit-summary:
        ./xunit-summary.py "corefx/tests"
 
+
+update-tests-coreclr: coreclr/.stamp-tests
+
+coreclr/.stamp-tests:
+       rm -rf coreclr/tests
+       curl -L "$(CORECLR_TESTS)" --output coreclr/tests.zip
+       $(UNZIPCMD) coreclr/tests.zip -C coreclr/
+       mkdir coreclr/tests
+       $(UNZIPCMD) coreclr/Tests_Linux_x64_checked/Linux.x64.Checked.tar.gz -C coreclr/tests
+       touch $@
+
+.PHONY: corerun
+corerun:
+       cp corerun/corerun $(SHAREDRUNTIME)
+
+run-tests-coreclr: prepare update-tests-coreclr corerun
+       rm -rf coreclr/output.log
+       @counter=0; \
+       failures=0; \
+       test_files=$$(find coreclr/tests -type f -name "*.sh"); \
+       for testdir in $$test_files; do \
+               counter=$$((counter+1)); \
+               echo "Running $$testdir"; \
+               if sh $$testdir -coreroot=$(realpath $(SHAREDRUNTIME)) >>coreclr/output.log 2>&1 ; then \
+                       echo "FAILED"; \
+                       failures=$$((failures+1)); \
+               fi; \
+       done; \
+       echo "Tests Count: $$counter"; \
+       echo "Failed: $$failures"
 endif
 
 distdir:
diff --git a/src/mono/netcore/corerun/.gitignore b/src/mono/netcore/corerun/.gitignore
new file mode 100644 (file)
index 0000000..23295f4
--- /dev/null
@@ -0,0 +1 @@
+/corerun
\ No newline at end of file
diff --git a/src/mono/netcore/corerun/Makefile.am b/src/mono/netcore/corerun/Makefile.am
new file mode 100644 (file)
index 0000000..d661854
--- /dev/null
@@ -0,0 +1,7 @@
+# Files copied from coreclr/src/coreclr/hosts/unixcorerun repo
+
+AM_CPPFLAGS = $(SHARED_CFLAGS)
+
+bin_PROGRAMS = corerun
+
+corerun_SOURCES = corerun.cpp coreruncommon.cpp coreruncommon.h coreclrhost.h
diff --git a/src/mono/netcore/corerun/coreclrhost.h b/src/mono/netcore/corerun/coreclrhost.h
new file mode 100644 (file)
index 0000000..d1ec724
--- /dev/null
@@ -0,0 +1,125 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+// APIs for hosting CoreCLR
+//
+
+#ifndef __CORECLR_HOST_H__
+#define __CORECLR_HOST_H__
+
+#if defined(_WIN32) && defined(_M_IX86)
+#define CORECLR_CALLING_CONVENTION __stdcall
+#else
+#define CORECLR_CALLING_CONVENTION
+#endif
+
+// For each hosting API, we define a function prototype and a function pointer
+// The prototype is useful for implicit linking against the dynamic coreclr
+// library and the pointer for explicit dynamic loading (dlopen, LoadLibrary)
+#define CORECLR_HOSTING_API(function, ...) \
+    extern "C" int CORECLR_CALLING_CONVENTION function(__VA_ARGS__); \
+    typedef int (CORECLR_CALLING_CONVENTION *function##_ptr)(__VA_ARGS__)
+    
+//
+// Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain
+//
+// Parameters:
+//  exePath                 - Absolute path of the executable that invoked the ExecuteAssembly (the native host application)
+//  appDomainFriendlyName   - Friendly name of the app domain that will be created to execute the assembly
+//  propertyCount           - Number of properties (elements of the following two arguments)
+//  propertyKeys            - Keys of properties of the app domain
+//  propertyValues          - Values of properties of the app domain
+//  hostHandle              - Output parameter, handle of the created host
+//  domainId                - Output parameter, id of the created app domain 
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+CORECLR_HOSTING_API(coreclr_initialize,
+            const char* exePath,
+            const char* appDomainFriendlyName,
+            int propertyCount,
+            const char** propertyKeys,
+            const char** propertyValues,
+            void** hostHandle,
+            unsigned int* domainId);
+
+//
+// Shutdown CoreCLR. It unloads the app domain and stops the CoreCLR host.
+//
+// Parameters:
+//  hostHandle              - Handle of the host
+//  domainId                - Id of the domain
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+CORECLR_HOSTING_API(coreclr_shutdown,
+            void* hostHandle,
+            unsigned int domainId);
+
+//
+// Shutdown CoreCLR. It unloads the app domain and stops the CoreCLR host.
+//
+// Parameters:
+//  hostHandle              - Handle of the host
+//  domainId                - Id of the domain
+//  latchedExitCode         - Latched exit code after domain unloaded
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+CORECLR_HOSTING_API(coreclr_shutdown_2,
+            void* hostHandle,
+            unsigned int domainId,
+            int* latchedExitCode);
+
+//
+// Create a native callable function pointer for a managed method.
+//
+// Parameters:
+//  hostHandle              - Handle of the host
+//  domainId                - Id of the domain 
+//  entryPointAssemblyName  - Name of the assembly which holds the custom entry point
+//  entryPointTypeName      - Name of the type which holds the custom entry point
+//  entryPointMethodName    - Name of the method which is the custom entry point
+//  delegate                - Output parameter, the function stores a native callable function pointer to the delegate at the specified address
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+CORECLR_HOSTING_API(coreclr_create_delegate,
+            void* hostHandle,
+            unsigned int domainId,
+            const char* entryPointAssemblyName,
+            const char* entryPointTypeName,
+            const char* entryPointMethodName,
+            void** delegate);
+
+//
+// Execute a managed assembly with given arguments
+//
+// Parameters:
+//  hostHandle              - Handle of the host
+//  domainId                - Id of the domain 
+//  argc                    - Number of arguments passed to the executed assembly
+//  argv                    - Array of arguments passed to the executed assembly
+//  managedAssemblyPath     - Path of the managed assembly to execute (or NULL if using a custom entrypoint).
+//  exitCode                - Exit code returned by the executed assembly
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+CORECLR_HOSTING_API(coreclr_execute_assembly,
+            void* hostHandle,
+            unsigned int domainId,
+            int argc,
+            const char** argv,
+            const char* managedAssemblyPath,
+            unsigned int* exitCode);
+
+#undef CORECLR_HOSTING_API
+                      
+#endif // __CORECLR_HOST_H__
diff --git a/src/mono/netcore/corerun/corerun.cpp b/src/mono/netcore/corerun/corerun.cpp
new file mode 100644 (file)
index 0000000..4e5c9d9
--- /dev/null
@@ -0,0 +1,162 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <coreruncommon.h>
+#include <string>
+#include <string.h>
+#include <sys/stat.h>
+
+// Display the command line options
+void DisplayUsage()
+{
+    fprintf(
+        stderr,
+        "Usage: corerun [OPTIONS] assembly [ARGUMENTS]\n"
+        "Execute the specified managed assembly with the passed in arguments\n\n"
+        "Options:\n"
+        "-c, --clr-path  path to the libcoreclr.so and the managed CLR assemblies\n");
+}
+
+// Parse the command line arguments 
+bool ParseArguments(
+        const int argc,
+        const char* argv[],
+        const char** clrFilesPath,
+        const char** managedAssemblyPath,
+        int* managedAssemblyArgc,
+        const char*** managedAssemblyArgv)
+{
+    bool success = false;
+
+    *clrFilesPath = nullptr;
+    *managedAssemblyPath = nullptr;
+    *managedAssemblyArgv = nullptr;
+    *managedAssemblyArgc = 0;
+
+    // The command line must contain at least the current exe name and the managed assembly path
+    if (argc >= 2)
+    {
+        for (int i = 1; i < argc; i++)
+        {
+            // Check for an option
+            if (argv[i][0] == '-')
+            {
+                // Path to the libcoreclr.so and the managed CLR assemblies
+                if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--clr-path") == 0)
+                {
+                    i++;
+                    if (i < argc)
+                    {
+                        *clrFilesPath = argv[i];
+                    }
+                    else
+                    {
+                        fprintf(stderr, "Option %s: missing path\n", argv[i - 1]);
+                        break;
+                    }
+                }
+                else if (strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
+                {
+                    DisplayUsage();
+                    break;
+                }
+                else
+                {
+                    fprintf(stderr, "Unknown option %s\n", argv[i]);
+                    break;
+                }
+            }
+            else
+            {
+                // First argument that is not an option is the managed assembly to execute
+                *managedAssemblyPath = argv[i];
+
+                int managedArgvOffset = (i + 1);
+                *managedAssemblyArgc = argc - managedArgvOffset;
+                if (*managedAssemblyArgc != 0)
+                {
+                    *managedAssemblyArgv = &argv[managedArgvOffset];
+                }
+                success = true;
+                break;
+            }
+        }
+    }
+    else
+    {
+        DisplayUsage();
+    }
+
+    return success;
+}
+
+int corerun(const int argc, const char* argv[])
+{
+    const char* clrFilesPath;
+    const char* managedAssemblyPath;
+    const char** managedAssemblyArgv;
+    int managedAssemblyArgc;
+
+    if (!ParseArguments(
+            argc,
+            argv,
+            &clrFilesPath,
+            &managedAssemblyPath,
+            &managedAssemblyArgc,
+            &managedAssemblyArgv))
+    {
+        // Invalid command line
+        return -1;
+    }
+
+    // Check if the specified managed assembly file exists
+    struct stat sb;
+    if (stat(managedAssemblyPath, &sb) == -1)
+    {
+        perror("Managed assembly not found");
+        return -1;
+    }
+
+    // Verify that the managed assembly path points to a file
+    if (!S_ISREG(sb.st_mode))
+    {
+        fprintf(stderr, "The specified managed assembly is not a file\n");
+        return -1;
+    }
+
+    // Make sure we have a full path for argv[0].
+    std::string argv0AbsolutePath;
+    if (!GetEntrypointExecutableAbsolutePath(argv0AbsolutePath))
+    {
+        perror("Could not get full path");
+        return -1;
+    }
+
+    std::string clrFilesAbsolutePath;
+    if(!GetClrFilesAbsolutePath(argv0AbsolutePath.c_str(), clrFilesPath, clrFilesAbsolutePath))
+    {
+        return -1;
+    }
+
+    std::string managedAssemblyAbsolutePath;
+    if (!GetAbsolutePath(managedAssemblyPath, managedAssemblyAbsolutePath))
+    {
+        perror("Failed to convert managed assembly path to absolute path");
+        return -1;
+    }
+
+    int exitCode = ExecuteManagedAssembly(
+                            argv0AbsolutePath.c_str(),
+                            clrFilesAbsolutePath.c_str(),
+                            managedAssemblyAbsolutePath.c_str(),
+                            managedAssemblyArgc,
+                            managedAssemblyArgv);
+
+    return exitCode;
+}
+
+int main(const int argc, const char* argv[])
+{
+    return corerun(argc, argv);
+}
diff --git a/src/mono/netcore/corerun/coreruncommon.cpp b/src/mono/netcore/corerun/coreruncommon.cpp
new file mode 100644 (file)
index 0000000..25007bb
--- /dev/null
@@ -0,0 +1,520 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+// Code that is used by both the Unix corerun and coreconsole.
+//
+
+#include "config.h"
+
+#include <cstdlib>
+#include <cstring>
+#include <assert.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <limits.h>
+#include <set>
+#include <string>
+#include <string.h>
+#include <sys/stat.h>
+#if defined(__FreeBSD__)
+#include <sys/types.h>
+#include <sys/param.h>
+#endif
+#if HAVE_GETAUXVAL
+#include <sys/auxv.h>
+#endif
+#if defined(HAVE_SYS_SYSCTL_H) || defined(__FreeBSD__)
+#include <sys/sysctl.h>
+#endif
+#include "coreruncommon.h"
+#include "coreclrhost.h"
+#include <unistd.h>
+#ifndef SUCCEEDED
+#define SUCCEEDED(Status) ((Status) >= 0)
+#endif // !SUCCEEDED
+
+// Name of the environment variable controlling server GC.
+// If set to 1, server GC is enabled on startup. If 0, server GC is
+// disabled. Server GC is off by default.
+static const char* serverGcVar = "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";
+
+#if defined(__linux__)
+#define symlinkEntrypointExecutable "/proc/self/exe"
+#elif !defined(__APPLE__)
+#define symlinkEntrypointExecutable "/proc/curproc/exe"
+#endif
+
+bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable)
+{
+    bool result = false;
+    
+    entrypointExecutable.clear();
+
+    // Get path to the executable for the current process using
+    // platform specific means.
+#if defined(__APPLE__)
+    
+    // On Mac, we ask the OS for the absolute path to the entrypoint executable
+    uint32_t lenActualPath = 0;
+    if (_NSGetExecutablePath(nullptr, &lenActualPath) == -1)
+    {
+        // OSX has placed the actual path length in lenActualPath,
+        // so re-attempt the operation
+        std::string resizedPath(lenActualPath, '\0');
+        char *pResizedPath = const_cast<char *>(resizedPath.c_str());
+        if (_NSGetExecutablePath(pResizedPath, &lenActualPath) == 0)
+        {
+            entrypointExecutable.assign(pResizedPath);
+            result = true;
+        }
+    }
+#elif defined (__FreeBSD__)
+    static const int name[] = {
+        CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1
+    };
+    char path[PATH_MAX];
+    size_t len;
+
+    len = sizeof(path);
+    if (sysctl(name, 4, path, &len, nullptr, 0) == 0)
+    {
+        entrypointExecutable.assign(path);
+        result = true;
+    }
+    else
+    {
+        // ENOMEM
+        result = false;
+    }
+#elif defined(__NetBSD__) && defined(KERN_PROC_PATHNAME)
+    static const int name[] = {
+        CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME,
+    };
+    char path[MAXPATHLEN];
+    size_t len;
+
+    len = sizeof(path);
+    if (sysctl(name, __arraycount(name), path, &len, NULL, 0) != -1)
+    {
+        entrypointExecutable.assign(path);
+        result = true;
+    }
+    else
+    {
+        result = false;
+    }
+#else
+
+#if HAVE_GETAUXVAL && defined(AT_EXECFN)
+    const char *execfn = (const char *)getauxval(AT_EXECFN);
+
+    if (execfn)
+    {
+        entrypointExecutable.assign(execfn);
+        result = true;
+    }
+    else
+#endif
+    // On other OSs, return the symlink that will be resolved by GetAbsolutePath
+    // to fetch the entrypoint EXE absolute path, inclusive of filename.
+    result = GetAbsolutePath(symlinkEntrypointExecutable, entrypointExecutable);
+#endif 
+
+    return result;
+}
+
+bool GetAbsolutePath(const char* path, std::string& absolutePath)
+{
+    bool result = false;
+
+    char realPath[PATH_MAX];
+    if (realpath(path, realPath) != nullptr && realPath[0] != '\0')
+    {
+        absolutePath.assign(realPath);
+        // realpath should return canonicalized path without the trailing slash
+        assert(absolutePath.back() != '/');
+
+        result = true;
+    }
+
+    return result;
+}
+
+bool GetDirectory(const char* absolutePath, std::string& directory)
+{
+    directory.assign(absolutePath);
+    size_t lastSlash = directory.rfind('/');
+    if (lastSlash != std::string::npos)
+    {
+        directory.erase(lastSlash);
+        return true;
+    }
+
+    return false;
+}
+
+bool GetClrFilesAbsolutePath(const char* currentExePath, const char* clrFilesPath, std::string& clrFilesAbsolutePath)
+{
+    std::string clrFilesRelativePath;
+    const char* clrFilesPathLocal = clrFilesPath;
+    if (clrFilesPathLocal == nullptr)
+    {
+        // There was no CLR files path specified, use the folder of the corerun/coreconsole
+        if (!GetDirectory(currentExePath, clrFilesRelativePath))
+        {
+            perror("Failed to get directory from argv[0]");
+            return false;
+        }
+
+        clrFilesPathLocal = clrFilesRelativePath.c_str();
+
+        // TODO: consider using an env variable (if defined) as a fall-back.
+        // The windows version of the corerun uses core_root env variable
+    }
+
+    if (!GetAbsolutePath(clrFilesPathLocal, clrFilesAbsolutePath))
+    {
+        perror("Failed to convert CLR files path to absolute path");
+        return false;
+    }
+
+    return true;
+}
+
+void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
+{
+    const char * const tpaExtensions[] = {
+                ".ni.dll",      // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+                ".dll",
+                ".ni.exe",
+                ".exe",
+                };
+
+    DIR* dir = opendir(directory);
+    if (dir == nullptr)
+    {
+        return;
+    }
+
+    std::set<std::string> addedAssemblies;
+
+    // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
+    // then files with .dll extension, etc.
+    for (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)
+        {
+            // We are interested in files only
+            switch (entry->d_type)
+            {
+            case DT_REG:
+                break;
+
+            // Handle symlinks and file systems that do not support d_type
+            case DT_LNK:
+            case DT_UNKNOWN:
+                {
+                    std::string fullFilename;
+
+                    fullFilename.append(directory);
+                    fullFilename.append("/");
+                    fullFilename.append(entry->d_name);
+
+                    struct stat sb;
+                    if (stat(fullFilename.c_str(), &sb) == -1)
+                    {
+                        continue;
+                    }
+
+                    if (!S_ISREG(sb.st_mode))
+                    {
+                        continue;
+                    }
+                }
+                break;
+
+            default:
+                continue;
+            }
+
+            std::string filename(entry->d_name);
+
+            // Check if the extension matches the one we are looking for
+            int extPos = filename.length() - extLength;
+            if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
+            {
+                continue;
+            }
+
+            std::string filenameWithoutExt(filename.substr(0, extPos));
+
+            // Make sure if we have an assembly with multiple extensions present,
+            // we insert only one version of it.
+            if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
+            {
+                addedAssemblies.insert(filenameWithoutExt);
+
+                tpaList.append(directory);
+                tpaList.append("/");
+                tpaList.append(filename);
+                tpaList.append(":");
+            }
+        }
+        
+        // Rewind the directory stream to be able to iterate over it for the next extension
+        rewinddir(dir);
+    }
+    
+    closedir(dir);
+}
+
+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 _ARM_
+    // libunwind library is used to unwind stack frame, but libunwind for ARM
+    // does not support ARM vfpv3/NEON registers in DWARF format correctly.
+    // Therefore let's disable stack unwinding using DWARF information
+    // See https://github.com/dotnet/coreclr/issues/6698
+    //
+    // libunwind use following methods to unwind stack frame.
+    // UNW_ARM_METHOD_ALL          0xFF
+    // UNW_ARM_METHOD_DWARF        0x01
+    // UNW_ARM_METHOD_FRAME        0x02
+    // UNW_ARM_METHOD_EXIDX        0x04
+    putenv(const_cast<char *>("UNW_ARM_UNWIND_METHOD=6"));
+#endif // _ARM_
+
+    std::string coreClrDllPath(clrFilesAbsolutePath);
+    coreClrDllPath.append("/");
+    coreClrDllPath.append(coreClrDll);
+
+    if (coreClrDllPath.length() >= PATH_MAX)
+    {
+        fprintf(stderr, "Absolute path to libcoreclr.so too long\n");
+        return -1;
+    }
+
+    // Get just the path component of the managed assembly path
+    std::string appPath;
+    GetDirectory(managedAssemblyAbsolutePath, appPath);
+
+    std::string tpaList;
+    if (strlen(managedAssemblyAbsolutePath) > 0)
+    {
+        // Target assembly should be added to the tpa list. Otherwise corerun.exe
+        // may find wrong assembly to execute.
+        // Details can be found at https://github.com/dotnet/coreclr/issues/5631
+        tpaList = managedAssemblyAbsolutePath;
+        tpaList.append(":");
+    }
+
+    // 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;
+}
diff --git a/src/mono/netcore/corerun/coreruncommon.h b/src/mono/netcore/corerun/coreruncommon.h
new file mode 100644 (file)
index 0000000..fb7f673
--- /dev/null
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <string>
+
+// Get the path to entrypoint executable
+bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable);
+
+// Get absolute path from the specified path.
+// Return true in case of a success, false otherwise.
+bool GetAbsolutePath(const char* path, std::string& absolutePath);
+
+// Get directory of the specified path.
+// Return true in case of a success, false otherwise.
+bool GetDirectory(const char* absolutePath, std::string& directory);
+
+//
+// Get the absolute path to use to locate libcoreclr.so and the CLR assemblies are stored. If clrFilesPath is provided,
+// this function will return the absolute path to it. Otherwise, the directory of the current executable is used.
+//
+// Return true in case of a success, false otherwise.
+//
+bool GetClrFilesAbsolutePath(const char* currentExePath, const char* clrFilesPath, std::string& clrFilesAbsolutePath);
+
+// Add all *.dll, *.ni.dll, *.exe, and *.ni.exe files from the specified directory to the tpaList string.
+void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList);
+
+//
+// Execute the specified managed assembly. 
+//
+// Parameters:
+//  currentExePath          - Path to the current executable
+//  clrFilesAbsolutePath    - Absolute path to the folder where the libcoreclr.so and CLR managed assemblies are stored
+//  managedAssemblyPath     - Path to the managed assembly to execute
+//  managedAssemblyArgc     - Number of arguments passed to the executed assembly
+//  managedAssemblyArgv     - Array of arguments passed to the executed assembly
+//
+// Returns:
+//  ExitCode of the assembly
+//
+int ExecuteManagedAssembly(
+            const char* currentExeAbsolutePath,
+            const char* clrFilesAbsolutePath,
+            const char* managedAssemblyAbsolutePath,
+            int managedAssemblyArgc,
+            const char** managedAssemblyArgv);
+
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+static const char * const coreClrDll = "libcoreclr.dylib";
+#else
+static const char * const coreClrDll = "libcoreclr.so";
+#endif
+