This change modifies the Unix hosting API so that the hosting app can create as many managed delegates as it needs and execute them as many times it wants. The new API contains separate functions to initialize and shutdown CoreCLR and a function to create a delegate. The current ExecuteAssembly function behavior stays unmodified for now to ensure that dnx that uses that API and that pulls the binary libcoreclr is not broken. After the dnx is updated to use the new coreclr_create_delegate API, I'll remove the ExecuteAssembly. Also done: 1) Added support for comments and skipping empty lines in the mscorwks_unixexports.src. 2) Restructured the mscorwks_unixexports.src 3) Added coreclr_execute_assembly to the unixinterface.cpp / exports 4) Modified coreruncommon.cpp to use the new hosting API
347 lines
11 KiB
C++
347 lines
11 KiB
C++
//
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
//
|
|
|
|
//
|
|
// Code that is used by both the Unix corerun and coreconsole.
|
|
//
|
|
|
|
#include <cstdlib>
|
|
#include <assert.h>
|
|
#include <dirent.h>
|
|
#include <dlfcn.h>
|
|
#include <limits.h>
|
|
#include <set>
|
|
#include <string>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
// The name of the CoreCLR native runtime DLL
|
|
#if defined(__APPLE__)
|
|
static const char * const coreClrDll = "libcoreclr.dylib";
|
|
#else
|
|
static const char * const coreClrDll = "libcoreclr.so";
|
|
#endif
|
|
|
|
#define SUCCEEDED(Status) ((Status) >= 0)
|
|
|
|
// Prototype of the coreclr_initialize function from the libcoreclr.so
|
|
typedef int (*InitializeCoreCLRFunction)(
|
|
const char* exePath,
|
|
const char* appDomainFriendlyName,
|
|
int propertyCount,
|
|
const char** propertyKeys,
|
|
const char** propertyValues,
|
|
void** hostHandle,
|
|
unsigned int* domainId);
|
|
|
|
// Prototype of the coreclr_shutdown function from the libcoreclr.so
|
|
typedef int (*ShutdownCoreCLRFunction)(
|
|
void* hostHandle,
|
|
unsigned int domainId);
|
|
|
|
// Prototype of the coreclr_execute_assembly function from the libcoreclr.so
|
|
typedef int (*ExecuteAssemblyFunction)(
|
|
void* hostHandle,
|
|
unsigned int domainId,
|
|
int argc,
|
|
const char** argv,
|
|
const char* managedAssemblyPath,
|
|
unsigned int* exitCode);
|
|
|
|
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;
|
|
|
|
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 nativeDllSearchDirs(appPath);
|
|
nativeDllSearchDirs.append(":");
|
|
nativeDllSearchDirs.append(clrFilesAbsolutePath);
|
|
|
|
std::string tpaList;
|
|
AddFilesFromDirectoryToTpaList(clrFilesAbsolutePath, tpaList);
|
|
|
|
void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW | RTLD_LOCAL);
|
|
if (coreclrLib != nullptr)
|
|
{
|
|
InitializeCoreCLRFunction initializeCoreCLR = (InitializeCoreCLRFunction)dlsym(coreclrLib, "coreclr_initialize");
|
|
ExecuteAssemblyFunction executeAssembly = (ExecuteAssemblyFunction)dlsym(coreclrLib, "coreclr_execute_assembly");
|
|
ShutdownCoreCLRFunction shutdownCoreCLR = (ShutdownCoreCLRFunction)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
|
|
{
|
|
// 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"
|
|
};
|
|
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"
|
|
};
|
|
|
|
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;
|
|
}
|