diff --git a/setup.py b/setup.py index c6e4007e6..dd88db6ec 100644 --- a/setup.py +++ b/setup.py @@ -362,8 +362,10 @@ def build_extension(self, ext): ), 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": @@ -403,6 +405,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..9d6ec9d8a --- /dev/null +++ b/src/coreclr/coreutils.c @@ -0,0 +1,372 @@ +// 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. + size_t extIndex; + for (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..cb1b9a412 --- /dev/null +++ b/src/coreclr/coreutils.h @@ -0,0 +1,47 @@ +#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); + +#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..1a8c62665 --- /dev/null +++ b/src/coreclr/pynetinit.c @@ -0,0 +1,418 @@ +#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; + + int ii; + for (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 122132513..2f8502c94 100644 --- a/src/runtime/Python.Runtime.15.csproj +++ b/src/runtime/Python.Runtime.15.csproj @@ -141,7 +141,8 @@ - + + diff --git a/tools/nuget/nuget.exe b/tools/nuget/nuget.exe index 463f8e137..a34c36752 100644 Binary files a/tools/nuget/nuget.exe and b/tools/nuget/nuget.exe differ