From c98d6fb2743ac812bf60fe465cb31dad313d49e8 Mon Sep 17 00:00:00 2001 From: David Joyce Date: Wed, 24 Jan 2018 17:23:17 +0000 Subject: [PATCH 1/4] Initial coreclr and build tooling Can load .NET Core into python Pass clr PyObject back to Python --- setup.py | 17 +- src/coreclr/clrmod.c | 70 +++++ src/coreclr/coreclrhost.h | 55 ++++ src/coreclr/coreutils.c | 371 ++++++++++++++++++++++++ src/coreclr/coreutils.h | 60 ++++ src/coreclr/pynetclr.h | 38 +++ src/coreclr/pynetinit.c | 417 +++++++++++++++++++++++++++ src/runtime/Python.Runtime.15.csproj | 2 +- tools/nuget/nuget.exe | Bin 4596440 -> 5059168 bytes 9 files changed, 1028 insertions(+), 2 deletions(-) create mode 100644 src/coreclr/clrmod.c create mode 100644 src/coreclr/coreclrhost.h create mode 100644 src/coreclr/coreutils.c create mode 100644 src/coreclr/coreutils.h create mode 100644 src/coreclr/pynetclr.h create mode 100644 src/coreclr/pynetinit.c diff --git a/setup.py b/setup.py index 4ec2a2113..83416bd6f 100644 --- a/setup.py +++ b/setup.py @@ -265,8 +265,10 @@ def build_extension(self, ext): subprocess.check_call(" ".join(cmd + ["/t:Build"]), shell=use_shell) if DEVTOOLS == "MsDev15" or DEVTOOLS == "dotnet": subprocess.check_call(" ".join(cmd + ['"/t:Console_15:publish;Python_EmbeddingTest_15:publish"', "/p:TargetFramework=netcoreapp2.0"]), shell=use_shell) - if DEVTOOLS == "Mono" or DEVTOOLS == "dotnet": + if DEVTOOLS == "Mono": self._build_monoclr() + if DEVTOOLS == "dotnet": + self._build_coreclr() def _get_manifest(self, build_dir): if DEVTOOLS != "MsDev" and DEVTOOLS != "MsDev15": @@ -306,6 +308,19 @@ def _build_monoclr(self): build_ext.build_ext.build_extension(self, clr_ext) + def _build_coreclr(self): + # build the clr python module + clr_ext = Extension( + "clr", + sources=[ + "src/coreclr/pynetinit.c", + "src/coreclr/clrmod.c", + "src/coreclr/coreutils.c", + ], + ) + + build_ext.build_ext.build_extension(self, clr_ext) + def _install_packages(self): """install packages using nuget""" use_shell = DEVTOOLS == "Mono" or DEVTOOLS == "dotnet" diff --git a/src/coreclr/clrmod.c b/src/coreclr/clrmod.c new file mode 100644 index 000000000..42cbb2d7c --- /dev/null +++ b/src/coreclr/clrmod.c @@ -0,0 +1,70 @@ +#include "pynetclr.h" + +/* List of functions defined in the module */ +static PyMethodDef clr_methods[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +PyDoc_STRVAR(clr_module_doc, + "clr facade module to initialize the CLR. It's later " + "replaced by the real clr module. This module has a facade " + "attribute to make it distinguishable from the real clr module." +); + +static PyNet_Args *pn_args; +char **environ = NULL; + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef clrdef = { + PyModuleDef_HEAD_INIT, + "clr", /* m_name */ + clr_module_doc, /* m_doc */ + -1, /* m_size */ + clr_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; +#endif + +static PyObject *_initclr(void) +{ + PyObject *m; + + /* Create the module and add the functions */ +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&clrdef); +#else + m = Py_InitModule3("clr", clr_methods, clr_module_doc); +#endif + if (m == NULL) + return NULL; + PyModule_AddObject(m, "facade", Py_True); + Py_INCREF(Py_True); + + pn_args = PyNet_Init(1); + if (pn_args->error) + { + return NULL; + } + + if (NULL != pn_args->module) + return pn_args->module; + + return m; +} + +#if PY_MAJOR_VERSION >= 3 +PyMODINIT_FUNC +PyInit_clr(void) +{ + return _initclr(); +} +#else +PyMODINIT_FUNC +initclr(void) +{ + _initclr(); +} +#endif diff --git a/src/coreclr/coreclrhost.h b/src/coreclr/coreclrhost.h new file mode 100644 index 000000000..0391ba38b --- /dev/null +++ b/src/coreclr/coreclrhost.h @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// APIs for hosting CoreCLR +// + +#ifndef __CORECLR_HOST_H__ +#define __CORECLR_HOST_H__ + +// For each hosting API, we define a function prototype and a function pointer +// The prototype is useful for implicit linking against the dynamic coreclr +// library and the pointer for explicit dynamic loading (dlopen, LoadLibrary) +#define CORECLR_HOSTING_API(function, ...) \ + int function(__VA_ARGS__); \ + typedef int (*function##_ptr)(__VA_ARGS__) + +CORECLR_HOSTING_API(coreclr_initialize, + const char* exePath, + const char* appDomainFriendlyName, + int propertyCount, + const char** propertyKeys, + const char** propertyValues, + void** hostHandle, + unsigned int* domainId); + +CORECLR_HOSTING_API(coreclr_shutdown, + void* hostHandle, + unsigned int domainId); + +CORECLR_HOSTING_API(coreclr_shutdown_2, + void* hostHandle, + unsigned int domainId, + int* latchedExitCode); + +CORECLR_HOSTING_API(coreclr_create_delegate, + void* hostHandle, + unsigned int domainId, + const char* entryPointAssemblyName, + const char* entryPointTypeName, + const char* entryPointMethodName, + void** delegate); + +CORECLR_HOSTING_API(coreclr_execute_assembly, + void* hostHandle, + unsigned int domainId, + int argc, + const char** argv, + const char* managedAssemblyPath, + unsigned int* exitCode); + +#undef CORECLR_HOSTING_API + +#endif // __CORECLR_HOST_H__ diff --git a/src/coreclr/coreutils.c b/src/coreclr/coreutils.c new file mode 100644 index 000000000..396334932 --- /dev/null +++ b/src/coreclr/coreutils.c @@ -0,0 +1,371 @@ +// 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. +// + +// https://github.com/dotnet/coreclr/blob/master/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) +#include +#include +#endif +#if defined(HAVE_SYS_SYSCTL_H) || defined(__FreeBSD__) +#include +#endif +#include +#include "coreutils.h" +#ifndef SUCCEEDED +#define SUCCEEDED(Status) ((Status) >= 0) +#endif // !SUCCEEDED + +#if defined(__linux__) +#define symlinkEntrypointExecutable "/proc/self/exe" +#elif !defined(__APPLE__) +#define symlinkEntrypointExecutable "/proc/curproc/exe" +#endif + +bool GetEntrypointExecutableAbsolutePath(char** entrypointExecutable) +{ + bool result = false; + + // Get path to the executable for the current process using + // platform specific means. +#if defined(__APPLE__) + + char path[PATH_MAX]; + // On Mac, we ask the OS for the absolute path to the entrypoint executable + size_t lenActualPath = sizeof(path); + if (_NSGetExecutablePath(path, &lenActualPath) == 0) + { + char* buf = strdup(path); + if (buf == NULL) + { + perror("Could not allocate buffer for path"); + return false; + } + result = true; + } + else + { + fprintf(stderr, "Path too long\n"); + return result; + } +#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) + { + char* buf = strdup(path); + if (buf == NULL) + { + perror("Could not allocate buffer for path"); + return false; + } + 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) + { + char* buf = strdup(path); + if (buf == NULL) + { + perror("Could not allocate buffer for path"); + return false; + } + result = true; + } + else + { + result = false; + } +#else + // 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, char** absolutePath) +{ + bool result = false; + + char realPath[PATH_MAX]; + if (realpath(path, realPath) != NULL && realPath[0] != '\0') + { + // realpath should return canonicalized path without the trailing slash + assert((realPath)[strlen(realPath)-1] != '/'); + + *absolutePath = strdup(realPath); + if (*absolutePath == NULL) + { + perror("Could not allocate buffer for path"); + return false; + } + + result = true; + } + + return result; +} + +bool GetDirectory(const char* absolutePath, char** directory) +{ + *directory = strdup(absolutePath); + if (*directory == NULL) + { + perror("Could not allocate buffer for path"); + return false; + } + + size_t len = strlen(*directory); + if((*directory)[len-1] == '/') + { + (*directory)[len-1] = '\0'; + return true; + } + + return false; +} + +bool GetClrFilesAbsolutePath(const char* currentExePath, const char* clrFilesPath, char** clrFilesAbsolutePath) +{ + char* clrFilesRelativePath = NULL; + const char* clrFilesPathLocal = clrFilesPath; + if (clrFilesPathLocal == NULL) + { + // There was no CLR files path specified, use the folder of the current exe + if (!GetDirectory(currentExePath, &clrFilesRelativePath)) + { + fprintf(stderr, "Failed to get directory\n"); + return false; + } + + clrFilesPathLocal = clrFilesRelativePath; + + // 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)) + { + fprintf(stderr, "Failed to convert CLR files path to absolute path\n"); + return false; + } + + return true; +} + +bool AssemblyAlreadyPresent(const char* addedAssemblies, const char* filenameWithoutExt) +{ + // Copy buffer as strtok munges input + char buf[strlen(addedAssemblies) + 1]; + strcpy(buf, addedAssemblies); + const char* token = strtok(buf, ":"); + + while (token != NULL) + { + if (strcmp(token, filenameWithoutExt) == 0) + { + return true; + } + token = strtok(NULL, ":"); + } + + return false; +} + +void AddFilesFromDirectoryToTpaList(const char* directory, char** 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 == NULL) + { + return; + } + + // Initially empty string + char* addedAssemblies = malloc(1); + if (addedAssemblies == NULL) + { + perror("Could not allocate buffer"); + closedir(dir); + return; + } + + addedAssemblies[0] = '\0'; + + // 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]; + size_t extLength = strlen(ext); + + struct dirent* entry; + + // For all entries in the directory + while ((entry = readdir(dir)) != NULL) + { + // 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: + { + char fullFilename[strlen(directory) + strlen(entry->d_name) + 2]; + strcpy(fullFilename, directory); + strcat(fullFilename, "/"); + strcat(fullFilename, entry->d_name); + + struct stat sb; + if (stat(fullFilename, &sb) == -1) + { + continue; + } + + if (!S_ISREG(sb.st_mode)) + { + continue; + } + } + break; + + default: + continue; + } + + const char* filename = entry->d_name; + + // Check if the extension matches the one we are looking for + int extPos = strlen(filename) - extLength; + const char* extLoc = filename + extPos; + if ((extPos <= 0) || (strncmp(extLoc, ext, extLength) != 0)) + { + continue; + } + + char filenameWithoutExt[strlen(filename) - extLength + 1]; + strncpy(filenameWithoutExt, filename, extPos); + filenameWithoutExt[extPos] = '\0'; + + // Make sure if we have an assembly with multiple extensions present, + // we insert only one version of it. + if (!AssemblyAlreadyPresent(addedAssemblies, filenameWithoutExt)) + { + char* buf = realloc( + addedAssemblies, + strlen(addedAssemblies) + strlen(filenameWithoutExt) + 2); + if (buf == NULL) + { + perror("Could not reallocate buffer"); + closedir(dir); + return; + } + addedAssemblies = buf; + + strcat(addedAssemblies, filenameWithoutExt); + strcat(addedAssemblies, ":"); + + buf = realloc( + *tpaList, + strlen(*tpaList) + strlen(directory) + strlen(filename) + 3); + if (buf == NULL) + { + perror("Could not reallocate buffer"); + free(addedAssemblies); + closedir(dir); + return; + } + *tpaList = buf; + + strcat(*tpaList, directory); + strcat(*tpaList, "/"); + strcat(*tpaList, filename); + strcat(*tpaList, ":"); + } + } + + // Rewind the directory stream to be able to iterate over it for the next extension + rewinddir(dir); + } + + free(addedAssemblies); + + closedir(dir); +} + + +const char* GetEnvValueBoolean(const char* envVariable) +{ + const char* envValue = getenv(envVariable); + if (envValue == NULL) + { + envValue = "0"; + } + + // CoreCLR expects strings "true" and "false" instead of "1" and "0". + if (strcmp(envValue, "1") == 0) + { + return "true"; + } + else + { + // Try again with lowercase + char* value = strdup(envValue); + if (value == NULL) + { + perror("Could not allocate buffer"); + return "false"; + } + + for (; *value; ++value) *value = tolower(*value); + + if (strcmp(value, "true") == 0) + { + return "true"; + } + } + + return "false"; +} diff --git a/src/coreclr/coreutils.h b/src/coreclr/coreutils.h new file mode 100644 index 000000000..7818d86fc --- /dev/null +++ b/src/coreclr/coreutils.h @@ -0,0 +1,60 @@ +#ifndef CORE_RUN_COMMON_H +#define CORE_RUN_COMMON_H + +#include + +// 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. + +// https://github.com/dotnet/coreclr/blob/master/src/coreclr/hosts/unixcoreruncommon/coreruncommon.h + +// Get the path to entrypoint executable +bool GetEntrypointExecutableAbsolutePath(char** entrypointExecutable); + +// Get absolute path from the specified path. +// Return true in case of a success, false otherwise. +bool GetAbsolutePath(const char* path, char** absolutePath); + +// Get directory of the specified path. +// Return true in case of a success, false otherwise. +bool GetDirectory(const char* absolutePath, char** 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, char** clrFilesAbsolutePath); + +// Check if the provided assembly is already included in the list of assemblies. +// Return true if present, false otherwise. +bool AssemblyAlreadyPresent(const char* addedAssemblies, const char* filenameWithoutExt); + +// Add all *.dll, *.ni.dll, *.exe, and *.ni.exe files from the specified directory to the tpaList string. +void AddFilesFromDirectoryToTpaList(const char* directory, char** tpaList); + +const char* GetEnvValueBoolean(const char* envVariable); + +#ifdef _WIN32 +char* strdup(const char* src) +{ + size_t len = strlen(src) + 1; + char* s = malloc(len); + if (s == NULL) + { + return NULL; + } + return (char *)memcpy(s, src, len); +} +#endif + +#if defined(__APPLE__) +#include +static const char * const coreClrDll = "libcoreclr.dylib"; +#else +static const char * const coreClrDll = "libcoreclr.so"; +#endif + +#endif // CORE_RUN_COMMON_H diff --git a/src/coreclr/pynetclr.h b/src/coreclr/pynetclr.h new file mode 100644 index 000000000..9b37b2515 --- /dev/null +++ b/src/coreclr/pynetclr.h @@ -0,0 +1,38 @@ +#ifndef PYNET_CLR_H +#define PYNET_CLR_H + +#include + +#define CLASS_NAME "Python.Runtime.PythonEngine" +#define ASSEMBLY_NAME "Python.Runtime" +#define PR_ASSEMBLY "Python.Runtime.dll" + +typedef void* (*py_init)(void); +typedef void (*py_finalize)(void); + +typedef struct +{ + char *pr_file; + char *error; + char *assembly_path; + char *assembly_name; + char *class_name; + char *init_method_name; + char *shutdown_method_name; + char *entry_path; + char *clr_path; + void* core_clr_lib; + void* host_handle; + unsigned int domain_id; + PyObject *module; + py_init init; + py_finalize shutdown; +} PyNet_Args; + +PyNet_Args *PyNet_Init(int); +void PyNet_Finalize(PyNet_Args *); + +void init(PyNet_Args *); +int createDelegates(PyNet_Args *); + +#endif // PYNET_CLR_H diff --git a/src/coreclr/pynetinit.c b/src/coreclr/pynetinit.c new file mode 100644 index 000000000..52bf180cc --- /dev/null +++ b/src/coreclr/pynetinit.c @@ -0,0 +1,417 @@ +#include "pynetclr.h" + +#include "coreclrhost.h" +#include "coreutils.h" +#include "stdlib.h" + +#ifndef _WIN32 +#include "dirent.h" +#include "dlfcn.h" +#include "libgen.h" +#include "alloca.h" +#endif + +#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"; + +// initialize Core CLR and PythonNet +PyNet_Args *PyNet_Init(int ext) +{ + PyNet_Args *pn_args = (PyNet_Args *)malloc(sizeof(PyNet_Args)); + + pn_args->pr_file = PR_ASSEMBLY; + pn_args->assembly_name = ASSEMBLY_NAME; + pn_args->class_name = CLASS_NAME; + + pn_args->error = NULL; + pn_args->shutdown = NULL; + pn_args->module = NULL; + + if (ext == 0) + { + pn_args->init_method_name = "Initialize"; + } + else + { + pn_args->init_method_name = "InitExt"; + } + + pn_args->shutdown_method_name = "Shutdown"; + + init(pn_args); + + if (pn_args->error != NULL) + { + PyErr_SetString(PyExc_ImportError, pn_args->error); + } + + return pn_args; +} + +// Shuts down PythonNet and cleans up Core CLR +void PyNet_Finalize(PyNet_Args *pn_args) +{ + // Indicates failure + int exitCode = -1; + + // Call Python.Runtime.PythonEngine.Shutdown() + if (pn_args->shutdown != NULL) + { + pn_args->shutdown(); + } + + // Shutdown Core CLR + if (pn_args->core_clr_lib) + { + coreclr_shutdown_2_ptr shutdownCoreCLR = (coreclr_shutdown_2_ptr)dlsym(pn_args->core_clr_lib, "coreclr_shutdown_2"); + + if (shutdownCoreCLR == NULL) + { + fprintf(stderr, "Function coreclr_shutdown_2 not found in the libcoreclr.so\n"); + } + else if (pn_args->host_handle && pn_args->domain_id) + { + int latchedExitCode = 0; + int st = shutdownCoreCLR(pn_args->host_handle, pn_args->domain_id, &latchedExitCode); + if (!SUCCEEDED(st)) + { + fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st); + exitCode = -1; + } + + if (exitCode != -1) + { + exitCode = latchedExitCode; + } + } + + if (dlclose(pn_args->core_clr_lib) != 0) + { + fprintf(stderr, "Warning - dlclose failed\n"); + } + } + + free(pn_args); +} + +void init(PyNet_Args* pn_args) +{ +#ifndef _WIN32 + //get python path system variable + PyObject *syspath = PySys_GetObject("path"); + pn_args->assembly_path = malloc(PATH_MAX); + const char *slash = "/"; + int found = 0; + + for (int ii = 0; ii < PyList_Size(syspath); ++ii) + { +#if PY_MAJOR_VERSION >= 3 + Py_ssize_t wlen; + wchar_t *wstr = PyUnicode_AsWideCharString(PyList_GetItem(syspath, ii), &wlen); + char *pydir = (char*)malloc(wlen + 1); + size_t mblen = wcstombs(pydir, wstr, wlen + 1); + if (mblen > wlen) + pydir[wlen] = '\0'; + PyMem_Free(wstr); +#else + const char *pydir = PyString_AsString(PyList_GetItem(syspath, ii)); +#endif + char *curdir = (char*) malloc(1024); + strncpy(curdir, strlen(pydir) > 0 ? pydir : ".", 1024); + strncat(curdir, slash, 1024); + +#if PY_MAJOR_VERSION >= 3 + free(pydir); +#endif + + //look in this directory for the pn_args->pr_file + DIR *dirp = opendir(curdir); + if (dirp != NULL) + { + struct dirent *dp; + while ((dp = readdir(dirp)) != NULL) + { + if (strcmp(dp->d_name, pn_args->pr_file) == 0) + { + strcpy(pn_args->assembly_path, curdir); + found = 1; + break; + } + } + closedir(dirp); + } + free(curdir); + + if (found) + { + break; + } + } + + if (!found) + { + fprintf(stderr, "Could not find assembly %s. \n", pn_args->pr_file); + return; + } +#endif + + if (!GetEntrypointExecutableAbsolutePath(&pn_args->entry_path)) + { + pn_args->error = "Unable to find entry point"; + return; + } + + if (!GetClrFilesAbsolutePath(pn_args->entry_path, "/usr/share/dotnet/shared/Microsoft.NETCore.App/2.0.0", &pn_args->clr_path)) + //if (!GetClrFilesAbsolutePath(pn_args->entry_path, NULL, &pn_args->clr_path))) + { + pn_args->error = "Unable to find clr path"; + return; + } + + int st = createDelegates(pn_args); + + if (SUCCEEDED(st)) + { + pn_args->module = pn_args->init(); + } + +#ifndef _WIN32 + free(pn_args->assembly_path); + free(pn_args->entry_path); +#endif +} + +int createDelegates(PyNet_Args * pn_args) +{ + // 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((char *)("UNW_ARM_UNWIND_METHOD=6")); +#endif // _ARM_ + + char coreClrDllPath[strlen(pn_args->clr_path) + strlen(coreClrDll) + 2]; + strcpy(coreClrDllPath, pn_args->clr_path); + strcat(coreClrDllPath, "/"); + strcat(coreClrDllPath, coreClrDll); + + if (strlen(coreClrDllPath) >= PATH_MAX) + { + fprintf(stderr, "Absolute path to libcoreclr.so too long\n"); + return -1; + } + + // Get just the path component of the managed assembly path + char* appPath = NULL; + if (!GetDirectory(pn_args->assembly_path, &appPath)) + { + return -1; + } + + char* tpaList = NULL; + if (pn_args->assembly_path != NULL) + { + // 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 = malloc(strlen(appPath) + strlen(pn_args->pr_file) + 3); + + if(tpaList == NULL) + { + perror("Could not allocate buffer"); + free(appPath); + return -1; + } + + strcpy(tpaList, appPath); + strcat(tpaList, "/"); + strcat(tpaList, pn_args->pr_file); + strcat(tpaList, ":"); + } + + // Construct native search directory paths + char* nativeDllSearchDirs = malloc(strlen(appPath) + strlen(pn_args->clr_path) + 2); + + if (nativeDllSearchDirs == NULL) + { + perror("Could not allocate buffer"); + free(appPath); + if (tpaList != NULL) + { + free(tpaList); + } + return -1; + } + + strcpy(nativeDllSearchDirs, appPath); + strcat(nativeDllSearchDirs, ":"); + strcat(nativeDllSearchDirs, pn_args->clr_path); + + const char *coreLibraries = getenv("CORE_LIBRARIES"); + if (coreLibraries) + { + nativeDllSearchDirs = realloc(nativeDllSearchDirs, strlen(coreLibraries) + 2); + + if (nativeDllSearchDirs == NULL) + { + perror("Could not reallocate buffer"); + free(appPath); + if (tpaList != NULL) + { + free(tpaList); + } + return -1; + } + + strcat(nativeDllSearchDirs, ":"); + strcat(nativeDllSearchDirs, coreLibraries); + if (strcmp(coreLibraries, pn_args->clr_path) != 0) + { + AddFilesFromDirectoryToTpaList(coreLibraries, &tpaList); + } + } + + AddFilesFromDirectoryToTpaList(pn_args->clr_path, &tpaList); + + pn_args->core_clr_lib = dlopen(coreClrDllPath, RTLD_NOW | RTLD_LOCAL); + + if (pn_args->core_clr_lib != NULL) + { + coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)dlsym(pn_args->core_clr_lib, "coreclr_initialize"); + coreclr_create_delegate_ptr createDelegate = (coreclr_create_delegate_ptr)dlsym(pn_args->core_clr_lib, "coreclr_create_delegate"); + + if (initializeCoreCLR == NULL) + { + fprintf(stderr, "Function coreclr_initialize not found in the libcoreclr.so\n"); + } + else if (createDelegate == NULL) + { + fprintf(stderr, "Function coreclr_create_delegate not found in the libcoreclr.so\n"); + } + else + { + // Check whether we are enabling server GC (off by default) + const char* useServerGc = GetEnvValueBoolean(serverGcVar); + + // 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", + "AppDomainCompatSwitch", + }; + const char *propertyValues[] = { + // TRUSTED_PLATFORM_ASSEMBLIES + tpaList, + // APP_PATHS + appPath, + // APP_NI_PATHS + appPath, + // NATIVE_DLL_SEARCH_DIRECTORIES + nativeDllSearchDirs, + // System.GC.Server + useServerGc, + // AppDomainCompatSwitch + "UseLatestBehaviorWhenTFMNotSpecified" + }; + + pn_args->host_handle = NULL; + pn_args->domain_id = 0; + + int st = initializeCoreCLR( + pn_args->entry_path, + "pythonnet", + sizeof(propertyKeys) / sizeof(propertyKeys[0]), + propertyKeys, + propertyValues, + &pn_args->host_handle, + &pn_args->domain_id); + + if (!SUCCEEDED(st)) + { + fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st); + exitCode = -1; + } + else + { + // Create init delegate + st = createDelegate( + pn_args->host_handle, + pn_args->domain_id, + pn_args->assembly_name, + pn_args->class_name, + pn_args->init_method_name, + (void**)&pn_args->init); + + if (!SUCCEEDED(st)) + { + fprintf(stderr, "coreclr_create_delegate failed - status: 0x%08x\n", st); + exitCode = -1; + } + + // Create shutdown delegate + st = createDelegate( + pn_args->host_handle, + pn_args->domain_id, + pn_args->assembly_name, + pn_args->class_name, + pn_args->shutdown_method_name, + (void**)&pn_args->shutdown); + + if (!SUCCEEDED(st)) + { + fprintf(stderr, "coreclr_create_delegate failed - status: 0x%08x\n", st); + exitCode = -1; + } + else + { + exitCode = 0; + } + } + } + } + else + { + const char* error = dlerror(); + fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error); + } + + free(appPath); + free(tpaList); + free(nativeDllSearchDirs); + + return exitCode; +} diff --git a/src/runtime/Python.Runtime.15.csproj b/src/runtime/Python.Runtime.15.csproj index cfde0a127..f03547804 100644 --- a/src/runtime/Python.Runtime.15.csproj +++ b/src/runtime/Python.Runtime.15.csproj @@ -134,7 +134,7 @@ - + diff --git a/tools/nuget/nuget.exe b/tools/nuget/nuget.exe index 463f8e137da6faa6695d029ff8b0e130ad31ebff..a34c36752a4c809be2eacaea88ca0c2b2fd7593d 100644 GIT binary patch literal 5059168 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

rd3~5;Z6^Nqo(DM?Ch*w=a-rd7m-vU3wwB2x= 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

~?`?S3ZMZ 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

(M^FRqi-4wlOx@W!v!w3-f%PBWEHmYOzH+!4 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

wTig+*af?U1)`_lB|*Eo`XDe;}eFA~qY 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