diff options
Diffstat (limited to 'src/coreclr/hosts/unixcoreruncommon')
-rw-r--r-- | src/coreclr/hosts/unixcoreruncommon/.gitmirror | 1 | ||||
-rw-r--r-- | src/coreclr/hosts/unixcoreruncommon/CMakeLists.txt | 12 | ||||
-rw-r--r-- | src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp | 454 | ||||
-rw-r--r-- | src/coreclr/hosts/unixcoreruncommon/coreruncommon.h | 56 |
4 files changed, 523 insertions, 0 deletions
diff --git a/src/coreclr/hosts/unixcoreruncommon/.gitmirror b/src/coreclr/hosts/unixcoreruncommon/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/coreclr/hosts/unixcoreruncommon/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/coreclr/hosts/unixcoreruncommon/CMakeLists.txt b/src/coreclr/hosts/unixcoreruncommon/CMakeLists.txt new file mode 100644 index 0000000000..a17e0a0fd6 --- /dev/null +++ b/src/coreclr/hosts/unixcoreruncommon/CMakeLists.txt @@ -0,0 +1,12 @@ +project(unixcoreruncommon) + +add_compile_options(-fPIC) + +_add_library(unixcoreruncommon + STATIC + coreruncommon.cpp +) + +if(CLR_CMAKE_PLATFORM_LINUX) + target_link_libraries(unixcoreruncommon dl) +endif(CLR_CMAKE_PLATFORM_LINUX) diff --git a/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp new file mode 100644 index 0000000000..b3a0c07a79 --- /dev/null +++ b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp @@ -0,0 +1,454 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// Code that is used by both the Unix corerun and coreconsole. +// + +#include <cstdlib> +#include <cstring> +#include <assert.h> +#include <dirent.h> +#include <dlfcn.h> +#include <limits.h> +#include <set> +#include <string> +#include <string.h> +#include <sys/stat.h> +#if defined(__FreeBSD__) +#include <sys/types.h> +#include <sys/param.h> +#endif +#if defined(HAVE_SYS_SYSCTL_H) || defined(__FreeBSD__) +#include <sys/sysctl.h> +#endif +#include "coreruncommon.h" +#include "coreclrhost.h" +#include <unistd.h> +#ifndef SUCCEEDED +#define SUCCEEDED(Status) ((Status) >= 0) +#endif // !SUCCEEDED + +// Name of the environment variable controlling server GC. +// If set to 1, server GC is enabled on startup. If 0, server GC is +// disabled. Server GC is off by default. +static const char* serverGcVar = "CORECLR_SERVER_GC"; + +#if defined(__linux__) +#define symlinkEntrypointExecutable "/proc/self/exe" +#elif !defined(__APPLE__) +#define symlinkEntrypointExecutable "/proc/curproc/exe" +#endif + +bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable) +{ + bool result = false; + + entrypointExecutable.clear(); + + // Get path to the executable for the current process using + // platform specific means. +#if defined(__linux__) || (defined(__NetBSD__) && !defined(KERN_PROC_PATHNAME)) + // On Linux, fetch the entry point EXE absolute path, inclusive of filename. + char exe[PATH_MAX]; + ssize_t res = readlink(symlinkEntrypointExecutable, exe, PATH_MAX - 1); + if (res != -1) + { + exe[res] = '\0'; + entrypointExecutable.assign(exe); + result = true; + } + else + { + result = false; + } +#elif defined(__APPLE__) + + // On Mac, we ask the OS for the absolute path to the entrypoint executable + uint32_t lenActualPath = 0; + if (_NSGetExecutablePath(nullptr, &lenActualPath) == -1) + { + // OSX has placed the actual path length in lenActualPath, + // so re-attempt the operation + std::string resizedPath(lenActualPath, '\0'); + char *pResizedPath = const_cast<char *>(resizedPath.c_str()); + if (_NSGetExecutablePath(pResizedPath, &lenActualPath) == 0) + { + entrypointExecutable.assign(pResizedPath); + result = true; + } + } +#elif defined (__FreeBSD__) + static const int name[] = { + CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 + }; + char path[PATH_MAX]; + size_t len; + + len = sizeof(path); + if (sysctl(name, 4, path, &len, nullptr, 0) == 0) + { + entrypointExecutable.assign(path); + result = true; + } + else + { + // ENOMEM + result = false; + } +#elif defined(__NetBSD__) && defined(KERN_PROC_PATHNAME) + static const int name[] = { + CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME, + }; + char path[MAXPATHLEN]; + size_t len; + + len = sizeof(path); + if (sysctl(name, __arraycount(name), path, &len, NULL, 0) != -1) + { + entrypointExecutable.assign(path); + result = true; + } + else + { + result = false; + } +#else + // On non-Mac OS, return the symlink that will be resolved by GetAbsolutePath + // to fetch the entrypoint EXE absolute path, inclusive of filename. + entrypointExecutable.assign(symlinkEntrypointExecutable); + result = true; +#endif + + return result; +} + +bool GetAbsolutePath(const char* path, std::string& absolutePath) +{ + bool result = false; + + char realPath[PATH_MAX]; + if (realpath(path, realPath) != nullptr && realPath[0] != '\0') + { + absolutePath.assign(realPath); + // realpath should return canonicalized path without the trailing slash + assert(absolutePath.back() != '/'); + + result = true; + } + + return result; +} + +bool GetDirectory(const char* absolutePath, std::string& directory) +{ + directory.assign(absolutePath); + size_t lastSlash = directory.rfind('/'); + if (lastSlash != std::string::npos) + { + directory.erase(lastSlash); + return true; + } + + return false; +} + +bool GetClrFilesAbsolutePath(const char* currentExePath, const char* clrFilesPath, std::string& clrFilesAbsolutePath) +{ + std::string clrFilesRelativePath; + const char* clrFilesPathLocal = clrFilesPath; + if (clrFilesPathLocal == nullptr) + { + // There was no CLR files path specified, use the folder of the corerun/coreconsole + if (!GetDirectory(currentExePath, clrFilesRelativePath)) + { + perror("Failed to get directory from argv[0]"); + return false; + } + + clrFilesPathLocal = clrFilesRelativePath.c_str(); + + // TODO: consider using an env variable (if defined) as a fall-back. + // The windows version of the corerun uses core_root env variable + } + + if (!GetAbsolutePath(clrFilesPathLocal, clrFilesAbsolutePath)) + { + perror("Failed to convert CLR files path to absolute path"); + return false; + } + + return true; +} + +void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList) +{ + const char * const tpaExtensions[] = { + ".ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir + ".dll", + ".ni.exe", + ".exe", + }; + + DIR* dir = opendir(directory); + if (dir == nullptr) + { + return; + } + + std::set<std::string> addedAssemblies; + + // Walk the directory for each extension separately so that we first get files with .ni.dll extension, + // then files with .dll extension, etc. + for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++) + { + const char* ext = tpaExtensions[extIndex]; + int extLength = strlen(ext); + + struct dirent* entry; + + // For all entries in the directory + while ((entry = readdir(dir)) != nullptr) + { + // We are interested in files only + switch (entry->d_type) + { + case DT_REG: + break; + + // Handle symlinks and file systems that do not support d_type + case DT_LNK: + case DT_UNKNOWN: + { + std::string fullFilename; + + fullFilename.append(directory); + fullFilename.append("/"); + fullFilename.append(entry->d_name); + + struct stat sb; + if (stat(fullFilename.c_str(), &sb) == -1) + { + continue; + } + + if (!S_ISREG(sb.st_mode)) + { + continue; + } + } + break; + + default: + continue; + } + + std::string filename(entry->d_name); + + // Check if the extension matches the one we are looking for + int extPos = filename.length() - extLength; + if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0)) + { + continue; + } + + std::string filenameWithoutExt(filename.substr(0, extPos)); + + // Make sure if we have an assembly with multiple extensions present, + // we insert only one version of it. + if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end()) + { + addedAssemblies.insert(filenameWithoutExt); + + tpaList.append(directory); + tpaList.append("/"); + tpaList.append(filename); + tpaList.append(":"); + } + } + + // Rewind the directory stream to be able to iterate over it for the next extension + rewinddir(dir); + } + + closedir(dir); +} + +int ExecuteManagedAssembly( + const char* currentExeAbsolutePath, + const char* clrFilesAbsolutePath, + const char* managedAssemblyAbsolutePath, + int managedAssemblyArgc, + const char** managedAssemblyArgv) +{ + // Indicates failure + int exitCode = -1; + +#ifdef _ARM_ + // libunwind library is used to unwind stack frame, but libunwind for ARM + // does not support ARM vfpv3/NEON registers in DWARF format correctly. + // Therefore let's disable stack unwinding using DWARF information + // See https://github.com/dotnet/coreclr/issues/6698 + // + // libunwind use following methods to unwind stack frame. + // UNW_ARM_METHOD_ALL 0xFF + // UNW_ARM_METHOD_DWARF 0x01 + // UNW_ARM_METHOD_FRAME 0x02 + // UNW_ARM_METHOD_EXIDX 0x04 + putenv(const_cast<char *>("UNW_ARM_UNWIND_METHOD=6")); +#endif // _ARM_ + + std::string coreClrDllPath(clrFilesAbsolutePath); + coreClrDllPath.append("/"); + coreClrDllPath.append(coreClrDll); + + if (coreClrDllPath.length() >= PATH_MAX) + { + fprintf(stderr, "Absolute path to libcoreclr.so too long\n"); + return -1; + } + + // Get just the path component of the managed assembly path + std::string appPath; + GetDirectory(managedAssemblyAbsolutePath, appPath); + + // Construct native search directory paths + std::string nativeDllSearchDirs(appPath); + char *coreLibraries = getenv("CORE_LIBRARIES"); + if (coreLibraries) + { + nativeDllSearchDirs.append(":"); + nativeDllSearchDirs.append(coreLibraries); + } + nativeDllSearchDirs.append(":"); + nativeDllSearchDirs.append(clrFilesAbsolutePath); + + std::string tpaList; + AddFilesFromDirectoryToTpaList(clrFilesAbsolutePath, tpaList); + + void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW | RTLD_LOCAL); + if (coreclrLib != nullptr) + { + coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)dlsym(coreclrLib, "coreclr_initialize"); + coreclr_execute_assembly_ptr executeAssembly = (coreclr_execute_assembly_ptr)dlsym(coreclrLib, "coreclr_execute_assembly"); + coreclr_shutdown_ptr shutdownCoreCLR = (coreclr_shutdown_ptr)dlsym(coreclrLib, "coreclr_shutdown"); + + if (initializeCoreCLR == nullptr) + { + fprintf(stderr, "Function coreclr_initialize not found in the libcoreclr.so\n"); + } + else if (executeAssembly == nullptr) + { + fprintf(stderr, "Function coreclr_execute_assembly not found in the libcoreclr.so\n"); + } + else if (shutdownCoreCLR == nullptr) + { + fprintf(stderr, "Function coreclr_shutdown not found in the libcoreclr.so\n"); + } + else + { + // Check whether we are enabling server GC (off by default) + const char* useServerGc = std::getenv(serverGcVar); + if (useServerGc == nullptr) + { + useServerGc = "0"; + } + + // CoreCLR expects strings "true" and "false" instead of "1" and "0". + useServerGc = std::strcmp(useServerGc, "1") == 0 ? "true" : "false"; + + // Allowed property names: + // APPBASE + // - The base path of the application from which the exe and other assemblies will be loaded + // + // TRUSTED_PLATFORM_ASSEMBLIES + // - The list of complete paths to each of the fully trusted assemblies + // + // APP_PATHS + // - The list of paths which will be probed by the assembly loader + // + // APP_NI_PATHS + // - The list of additional paths that the assembly loader will probe for ngen images + // + // NATIVE_DLL_SEARCH_DIRECTORIES + // - The list of paths that will be probed for native DLLs called by PInvoke + // + const char *propertyKeys[] = { + "TRUSTED_PLATFORM_ASSEMBLIES", + "APP_PATHS", + "APP_NI_PATHS", + "NATIVE_DLL_SEARCH_DIRECTORIES", + "AppDomainCompatSwitch", + "System.GC.Server", + }; + const char *propertyValues[] = { + // TRUSTED_PLATFORM_ASSEMBLIES + tpaList.c_str(), + // APP_PATHS + appPath.c_str(), + // APP_NI_PATHS + appPath.c_str(), + // NATIVE_DLL_SEARCH_DIRECTORIES + nativeDllSearchDirs.c_str(), + // AppDomainCompatSwitch + "UseLatestBehaviorWhenTFMNotSpecified", + // System.GC.Server + useServerGc, + }; + + void* hostHandle; + unsigned int domainId; + + int st = initializeCoreCLR( + currentExeAbsolutePath, + "unixcorerun", + sizeof(propertyKeys) / sizeof(propertyKeys[0]), + propertyKeys, + propertyValues, + &hostHandle, + &domainId); + + if (!SUCCEEDED(st)) + { + fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st); + exitCode = -1; + } + else + { + st = executeAssembly( + hostHandle, + domainId, + managedAssemblyArgc, + managedAssemblyArgv, + managedAssemblyAbsolutePath, + (unsigned int*)&exitCode); + + if (!SUCCEEDED(st)) + { + fprintf(stderr, "coreclr_execute_assembly failed - status: 0x%08x\n", st); + exitCode = -1; + } + + st = shutdownCoreCLR(hostHandle, domainId); + if (!SUCCEEDED(st)) + { + fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st); + exitCode = -1; + } + } + } + + if (dlclose(coreclrLib) != 0) + { + fprintf(stderr, "Warning - dlclose failed\n"); + } + } + else + { + char* error = dlerror(); + fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error); + } + + return exitCode; +} diff --git a/src/coreclr/hosts/unixcoreruncommon/coreruncommon.h b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.h new file mode 100644 index 0000000000..fb7f6730b9 --- /dev/null +++ b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.h @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include <string> + +// Get the path to entrypoint executable +bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable); + +// Get absolute path from the specified path. +// Return true in case of a success, false otherwise. +bool GetAbsolutePath(const char* path, std::string& absolutePath); + +// Get directory of the specified path. +// Return true in case of a success, false otherwise. +bool GetDirectory(const char* absolutePath, std::string& directory); + +// +// Get the absolute path to use to locate libcoreclr.so and the CLR assemblies are stored. If clrFilesPath is provided, +// this function will return the absolute path to it. Otherwise, the directory of the current executable is used. +// +// Return true in case of a success, false otherwise. +// +bool GetClrFilesAbsolutePath(const char* currentExePath, const char* clrFilesPath, std::string& clrFilesAbsolutePath); + +// Add all *.dll, *.ni.dll, *.exe, and *.ni.exe files from the specified directory to the tpaList string. +void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList); + +// +// Execute the specified managed assembly. +// +// Parameters: +// currentExePath - Path to the current executable +// clrFilesAbsolutePath - Absolute path to the folder where the libcoreclr.so and CLR managed assemblies are stored +// managedAssemblyPath - Path to the managed assembly to execute +// managedAssemblyArgc - Number of arguments passed to the executed assembly +// managedAssemblyArgv - Array of arguments passed to the executed assembly +// +// Returns: +// ExitCode of the assembly +// +int ExecuteManagedAssembly( + const char* currentExeAbsolutePath, + const char* clrFilesAbsolutePath, + const char* managedAssemblyAbsolutePath, + int managedAssemblyArgc, + const char** managedAssemblyArgv); + + +#if defined(__APPLE__) +#include <mach-o/dyld.h> +static const char * const coreClrDll = "libcoreclr.dylib"; +#else +static const char * const coreClrDll = "libcoreclr.so"; +#endif + |