added first version of custom PS hosting code
This commit is contained in:
parent
a756ecaadf
commit
3711159996
@ -7,16 +7,21 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
include_directories(../ext-src/gtest/fused-src impl)
|
||||
link_directories(${monad_native_BINARY_DIR})
|
||||
|
||||
# source file definitions
|
||||
set(LIB_SOURCE_FILES impl/getcurrentprocessorid.cpp impl/getusername.cpp impl/terminal.cpp)
|
||||
set(TEST_SOURCE_FILES tests/test-getcurrentprocessid.cpp)
|
||||
set(SOURCE_FILES main.cpp ../ext-src/gtest/fused-src/gtest/gtest-all.cc)
|
||||
SET(HOST_CMDLINE_SOURCE_FILES host/cmdline/main.cpp host/cmdline/coreclrutil.cpp)
|
||||
|
||||
# target definitions
|
||||
add_library(ps SHARED ${LIB_SOURCE_FILES})
|
||||
add_executable(monad_native ${SOURCE_FILES} ${TEST_SOURCE_FILES})
|
||||
add_executable(host_cmdline ${HOST_CMDLINE_SOURCE_FILES})
|
||||
|
||||
# add pthread and other libs
|
||||
find_package(Threads)
|
||||
target_link_libraries(monad_native ${CMAKE_THREAD_LIBS_INIT} ps)
|
||||
target_link_libraries(host_cmdline dl)
|
||||
|
||||
|
||||
|
||||
|
163
src/host/cmdline/coreclrutil.cpp
Normal file
163
src/host/cmdline/coreclrutil.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
#include "coreclrutil.h"
|
||||
#include <dirent.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <set>
|
||||
|
||||
namespace CoreCLRUtil
|
||||
{
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace CoreCLRUtil
|
||||
|
63
src/host/cmdline/coreclrutil.h
Normal file
63
src/host/cmdline/coreclrutil.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace CoreCLRUtil
|
||||
{
|
||||
|
||||
//
|
||||
// This code is mostly copied and modified from original CoreCLR project's
|
||||
// code on github: https://github.com/dotnet/coreclr
|
||||
//
|
||||
|
||||
//
|
||||
// these function signatures are the entry point API for CoreCLR
|
||||
//
|
||||
|
||||
// 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);
|
||||
|
||||
// Prototype of coreclr_create_delegate function from the libcoreclr.so
|
||||
typedef int (*CreateDelegateFunction)(
|
||||
void* hostHandle,
|
||||
unsigned int domainId,
|
||||
const char* entryPointAssemblyName,
|
||||
const char* entryPointTypeName,
|
||||
const char* entryPointMethodName,
|
||||
void** delegate);
|
||||
|
||||
// The name of the CoreCLR native runtime DLL
|
||||
#if defined(__APPLE__)
|
||||
constexpr char coreClrDll[] = "libcoreclr.dylib";
|
||||
#else
|
||||
constexpr char coreClrDll[] = "libcoreclr.so";
|
||||
#endif
|
||||
|
||||
bool GetAbsolutePath(const char* path, std::string& absolutePath);
|
||||
bool GetDirectory(const char* absolutePath, std::string& directory);
|
||||
bool GetClrFilesAbsolutePath(const char* currentExePath, const char* clrFilesPath, std::string& clrFilesAbsolutePath);
|
||||
void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList);
|
||||
|
||||
} // namespace CoreCLRUtil
|
||||
|
194
src/host/cmdline/main.cpp
Normal file
194
src/host/cmdline/main.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "coreclrutil.h"
|
||||
#include <limits.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// TODO: read from command line args
|
||||
std::string clrAbsolutePath = "/home/peter/gitwd/monad-linux/scripts/exec_env/app_base";
|
||||
|
||||
// managed assembly arguments
|
||||
int managedAssemblyArgc = 0;
|
||||
const char* managedAssemblyArgv[] = { "" };
|
||||
|
||||
// there are two assemblies involved in hosting PowerShell:
|
||||
// - Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll
|
||||
// + this assembly has to be listed as platform assembly
|
||||
// - System.Management.Automation.dll
|
||||
std::string powershellBaseAbsolutePath = "/home/peter/gitwd/monad-linux/scripts/exec_env/app_base";
|
||||
std::string assemblyLoadContextAssemblyName = "Microsoft.PowerShell.CoreCLR.AssemblyLoadContext";
|
||||
std::string assemblyLoadContextAbsolutePath = powershellBaseAbsolutePath + "/" + assemblyLoadContextAssemblyName + ".dll";
|
||||
std::string systemManagementAutomationAssemblyName = "System.Management.Automation";
|
||||
std::string systemManagementAutomationAbsolutePath = powershellBaseAbsolutePath + "/" + systemManagementAutomationAssemblyName + ".dll";
|
||||
|
||||
std::string coreClrDllPath = clrAbsolutePath + "/" + CoreCLRUtil::coreClrDll;
|
||||
if (coreClrDllPath.size() >= PATH_MAX)
|
||||
{
|
||||
std::cerr << "Absolute path to CoreCLR library too long" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::cout << "coreClrDllPath: " << coreClrDllPath << std::endl;
|
||||
|
||||
// TPA list
|
||||
//
|
||||
// The list of platform assemblies must include all CoreCLR assemblies
|
||||
// and the Microsoft.PowerShell.CoreCLR.AssemblyLoadContext
|
||||
//
|
||||
// TODO: move CLR assemblies to separate path during build&run make steps
|
||||
// TODO: only add assembly load context to TPA list, not any other PS dll
|
||||
|
||||
std::string tpaList;
|
||||
CoreCLRUtil::AddFilesFromDirectoryToTpaList(clrAbsolutePath.c_str(),tpaList);
|
||||
std::cout << "tpaList: " << tpaList << std::endl;
|
||||
|
||||
// assembly load paths
|
||||
//
|
||||
// All PowerShell assemblies are assumed to be in the same path
|
||||
|
||||
std::string appPath;
|
||||
if (!CoreCLRUtil::GetDirectory(assemblyLoadContextAbsolutePath.c_str(),appPath))
|
||||
{
|
||||
std::cerr << "failed to get assembly search directory from assembly absolute path" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::cout << "appPath: " << appPath << std::endl;
|
||||
|
||||
// search paths for native dlls
|
||||
//
|
||||
// Add both the CoreCLR directory and the PowerShell directory to this list
|
||||
std::string nativeDllSearchDirs = appPath + ":" + clrAbsolutePath;
|
||||
|
||||
// get the absolute path of the current executable
|
||||
std::string currentExeAbsolutePath;
|
||||
if (!CoreCLRUtil::GetAbsolutePath(argv[0],currentExeAbsolutePath))
|
||||
{
|
||||
std::cerr << "could not get absolute path of current executable" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// open the shared library
|
||||
void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW|RTLD_LOCAL);
|
||||
if (coreclrLib == nullptr)
|
||||
{
|
||||
char* error = dlerror();
|
||||
std::cerr << "dlopen failed to open the CoreCLR library: " << error << std::endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// query the function pointers
|
||||
CoreCLRUtil::InitializeCoreCLRFunction initializeCoreCLR = (CoreCLRUtil::InitializeCoreCLRFunction)dlsym(coreclrLib,"coreclr_initialize");
|
||||
CoreCLRUtil::ExecuteAssemblyFunction executeAssembly = (CoreCLRUtil::ExecuteAssemblyFunction)dlsym(coreclrLib,"coreclr_execute_assembly");
|
||||
CoreCLRUtil::ShutdownCoreCLRFunction shutdownCoreCLR = (CoreCLRUtil::ShutdownCoreCLRFunction)dlsym(coreclrLib,"coreclr_shutdown");
|
||||
CoreCLRUtil::CreateDelegateFunction createDelegate = (CoreCLRUtil::CreateDelegateFunction)dlsym(coreclrLib,"coreclr_create_delegate");
|
||||
|
||||
if (initializeCoreCLR == nullptr)
|
||||
{
|
||||
std::cerr << "function coreclr_initialize not found in CoreCLR library" << std::endl;
|
||||
return 3;
|
||||
}
|
||||
if (executeAssembly == nullptr)
|
||||
{
|
||||
std::cerr << "function coreclr_execute_assembly not found in CoreCLR library" << std::endl;
|
||||
return 3;
|
||||
}
|
||||
if (shutdownCoreCLR == nullptr)
|
||||
{
|
||||
std::cerr << "function coreclr_shutdown not found in CoreCLR library" << std::endl;
|
||||
return 3;
|
||||
}
|
||||
if (createDelegate == nullptr)
|
||||
{
|
||||
std::cerr << "function coreclr_create_delegate not found in CoreCLR library" << std::endl;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// create list of properties to initialize CoreCLR
|
||||
const char* propertyKeys[] = {
|
||||
"TRUSTED_PLATFORM_ASSEMBLIES",
|
||||
"APP_PATHS",
|
||||
"APP_NI_PATHS",
|
||||
"NATIVE_DLL_SEARCH_DIRECTORIES",
|
||||
"AppDomainCompatSwitch"
|
||||
};
|
||||
|
||||
const char* propertyValues[] = {
|
||||
tpaList.c_str(),
|
||||
appPath.c_str(),
|
||||
appPath.c_str(),
|
||||
nativeDllSearchDirs.c_str(),
|
||||
"UseLatestBehaviorWhenTFMNotSpecified"
|
||||
};
|
||||
|
||||
|
||||
// initialize CoreCLR
|
||||
void* hostHandle;
|
||||
unsigned int domainId;
|
||||
int status = initializeCoreCLR(
|
||||
currentExeAbsolutePath.c_str(),
|
||||
"ps_cmdline_host",
|
||||
sizeof(propertyKeys)/sizeof(propertyKeys[0]),
|
||||
propertyKeys,
|
||||
propertyValues,
|
||||
&hostHandle,
|
||||
&domainId);
|
||||
|
||||
if (0 > status)
|
||||
{
|
||||
std::cerr << "coreclr_initialize failed - status: " << std::hex << status << std::endl;
|
||||
return 4;
|
||||
}
|
||||
|
||||
// initialize the PS's custom assembly load context
|
||||
typedef void (*LoaderRunHelperFp)(const char16_t* appPath);
|
||||
LoaderRunHelperFp loaderDelegate = nullptr;
|
||||
status = createDelegate(
|
||||
hostHandle,
|
||||
domainId,
|
||||
"Microsoft.PowerShell.CoreCLR.AssemblyLoadContext, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
|
||||
"System.Management.Automation.PowerShellAssemblyLoadContextInitializer",
|
||||
"SetPowerShellAssemblyLoadContext",
|
||||
(void**)&loaderDelegate);
|
||||
if (0 > status)
|
||||
{
|
||||
std::cerr << "could not create delegate for SetPowerShellAssemblyLoadContext - status: " << std::hex << status << std::endl;
|
||||
return 4;
|
||||
}
|
||||
|
||||
loaderDelegate(u"/home/peter/gitwd/monad-linux/scripts/exec_env/app_base");
|
||||
|
||||
|
||||
typedef int (*TestDelegate)();
|
||||
TestDelegate testDelegate = nullptr;
|
||||
status = createDelegate(
|
||||
hostHandle,
|
||||
domainId,
|
||||
"System.Management.Automation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
|
||||
"System.Management.Automation.Platform",
|
||||
"IsLinux",
|
||||
(void**)&testDelegate);
|
||||
int returnValue = testDelegate();
|
||||
std::cout << "returnValue=" << returnValue << std::endl;
|
||||
|
||||
|
||||
typedef void (*UnmanagedEntry)();
|
||||
UnmanagedEntry unmanagedEntry = nullptr;
|
||||
status = createDelegate(
|
||||
hostHandle,
|
||||
domainId,
|
||||
"powershell-simple, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
|
||||
"ps_hello_world.Program",
|
||||
"UnmanagedEntry",
|
||||
(void**)&unmanagedEntry);
|
||||
if (0 > status)
|
||||
{
|
||||
std::cerr << "could not create delegate for UnmanagedEntry - status: " << std::hex << status << std::endl;
|
||||
return 4;
|
||||
}
|
||||
|
||||
unmanagedEntry();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user