From 0c5f78a223210371576a5c5ec7cda2ae035196bc Mon Sep 17 00:00:00 2001 From: Pete Baker Date: Thu, 19 Mar 2015 13:09:36 -0700 Subject: [PATCH 1/9] built and tested - pass --- impl/getcurrentthreadid.cpp | 9 +++++++++ impl/getcurrentthreadid.h | 10 ++++++++++ tests/test-getcurrentthreadid.cpp | 16 ++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 impl/getcurrentthreadid.cpp create mode 100644 impl/getcurrentthreadid.h create mode 100644 tests/test-getcurrentthreadid.cpp diff --git a/impl/getcurrentthreadid.cpp b/impl/getcurrentthreadid.cpp new file mode 100644 index 000000000..efd7a3136 --- /dev/null +++ b/impl/getcurrentthreadid.cpp @@ -0,0 +1,9 @@ +#include "getcurrentthreadid.h" +#include +#include + +HANDLE GetCurrentThreadId() +{ + pid_t tid = pthread_self(); + return reinterpret_cast(tid); +} diff --git a/impl/getcurrentthreadid.h b/impl/getcurrentthreadid.h new file mode 100644 index 000000000..258c3c397 --- /dev/null +++ b/impl/getcurrentthreadid.h @@ -0,0 +1,10 @@ +#pragma once + +#include "pal.h" + +PAL_BEGIN_EXTERNC + +HANDLE GetCurrentThreadId(); + +PAL_END_EXTERNC + diff --git a/tests/test-getcurrentthreadid.cpp b/tests/test-getcurrentthreadid.cpp new file mode 100644 index 000000000..7488d192d --- /dev/null +++ b/tests/test-getcurrentthreadid.cpp @@ -0,0 +1,16 @@ +#include +#include "getcurrentthreadid.h" +#include + +TEST(GetCurrentThreadId,simple) +{ + const HANDLE currentThreadId = GetCurrentThreadId(); + const pid_t tid = pthread_self(); + + // first make sure that on this platform those types are of the same size + ASSERT_TRUE(sizeof(HANDLE) >= sizeof(pid_t)); + + // now compare the actual values + ASSERT_EQ(currentThreadId,reinterpret_cast(tid)); +} + From 1b02619b2d513916106c8a0e1a827c92fcdd2bc3 Mon Sep 17 00:00:00 2001 From: Peter Honeder Date: Tue, 16 Jun 2015 14:02:19 -0700 Subject: [PATCH 2/9] added libps.so, added stub for GetUserName --- CMakeLists.txt | 13 +++++++++---- impl/getusername.cpp | 8 ++++++++ impl/getusername.h | 10 ++++++++++ impl/pal.h | 4 +++- 4 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 impl/getusername.cpp create mode 100644 impl/getusername.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4355f98d0..967a6f004 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,13 +5,18 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") #include gtest include_directories(../ext-src/gtest/fused-src impl) +link_directories(${monad_native_BINARY_DIR}) -set(SOURCE_FILES main.cpp tests/test-getcurrentprocessid.cpp impl/getcurrentprocessorid.cpp ../ext-src/gtest/fused-src/gtest/gtest-all.cc) -add_executable(monad_native ${SOURCE_FILES}) +set(LIB_SOURCE_FILES impl/getcurrentprocessorid.cpp impl/getusername.cpp) +set(TEST_SOURCE_FILES tests/test-getcurrentprocessid.cpp) +set(SOURCE_FILES main.cpp ../ext-src/gtest/fused-src/gtest/gtest-all.cc) -# add pthread +add_library(ps SHARED ${LIB_SOURCE_FILES}) +add_executable(monad_native ${SOURCE_FILES} ${TEST_SOURCE_FILES}) + +# add pthread and other libs find_package(Threads) -target_link_libraries(monad_native ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(monad_native ${CMAKE_THREAD_LIBS_INIT} ps) diff --git a/impl/getusername.cpp b/impl/getusername.cpp new file mode 100644 index 000000000..b37b1cffc --- /dev/null +++ b/impl/getusername.cpp @@ -0,0 +1,8 @@ +#include "getusername.h" +#include + +BOOL GetUserName(WCHAR_T* userName, UINT32* maxLength) +{ + return 0; +} + diff --git a/impl/getusername.h b/impl/getusername.h new file mode 100644 index 000000000..922a4e96a --- /dev/null +++ b/impl/getusername.h @@ -0,0 +1,10 @@ +#pragma once + +#include "pal.h" + +PAL_BEGIN_EXTERNC + +BOOL GetUserName(WCHAR_T* userName, UINT32* maxLength); + +PAL_END_EXTERNC + diff --git a/impl/pal.h b/impl/pal.h index 659afd4f6..1cfc77630 100644 --- a/impl/pal.h +++ b/impl/pal.h @@ -51,7 +51,9 @@ #define WIN32_FROM_HRESULT(hr) hr #define HRESULT_FROM_WIN32(error) error typedef unsigned long DWORD, *LPDWORD; - typedef int BOOL; + typedef char BOOL; + typedef unsigned short WCHAR_T; + typedef unsigned int UINT32; typedef unsigned long HRESULT; typedef const wchar_t *PCWSTR; typedef wchar_t *PWSTR; From 39e939ce0f7bfd1c90e949cd447ad71dc91fa8b1 Mon Sep 17 00:00:00 2001 From: Peter Honeder Date: Tue, 7 Jul 2015 18:04:23 -0700 Subject: [PATCH 3/9] added pinvoke functions to query terminal size --- CMakeLists.txt | 2 +- impl/pal.h | 1 + impl/terminal.cpp | 21 +++++++++++++++++++++ impl/terminal.h | 11 +++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 impl/terminal.cpp create mode 100644 impl/terminal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 967a6f004..f23a2b711 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") include_directories(../ext-src/gtest/fused-src impl) link_directories(${monad_native_BINARY_DIR}) -set(LIB_SOURCE_FILES impl/getcurrentprocessorid.cpp impl/getusername.cpp) +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) diff --git a/impl/pal.h b/impl/pal.h index 1cfc77630..f98108884 100644 --- a/impl/pal.h +++ b/impl/pal.h @@ -54,6 +54,7 @@ typedef char BOOL; typedef unsigned short WCHAR_T; typedef unsigned int UINT32; + typedef int INT32; typedef unsigned long HRESULT; typedef const wchar_t *PCWSTR; typedef wchar_t *PWSTR; diff --git a/impl/terminal.cpp b/impl/terminal.cpp new file mode 100644 index 000000000..c32192802 --- /dev/null +++ b/impl/terminal.cpp @@ -0,0 +1,21 @@ +#include "terminal.h" +#include + +INT32 GetTerminalWidth() +{ + struct winsize ws; + if (-1 == ioctl(0,TIOCGWINSZ,&ws)) + return -1; + + return ws.ws_col; +} + +INT32 GetTerminalHeight() +{ + struct winsize ws; + if (-1 == ioctl(0,TIOCGWINSZ,&ws)) + return -1; + + return ws.ws_row; +} + diff --git a/impl/terminal.h b/impl/terminal.h new file mode 100644 index 000000000..00332e9bf --- /dev/null +++ b/impl/terminal.h @@ -0,0 +1,11 @@ +#pragma once + +#include "pal.h" + +PAL_BEGIN_EXTERNC + +INT32 GetTerminalWidth(); +INT32 GetTerminalHeight(); + +PAL_END_EXTERNC + From b9784906f4d71d4d4a4f36f6667d6ed8e1d2aff9 Mon Sep 17 00:00:00 2001 From: Peter Honeder Date: Mon, 27 Jul 2015 17:38:11 +0200 Subject: [PATCH 4/9] added first version of custom PS hosting code --- CMakeLists.txt | 5 + host/cmdline/coreclrutil.cpp | 163 +++++++++++++++++++++++++++++ host/cmdline/coreclrutil.h | 63 ++++++++++++ host/cmdline/main.cpp | 194 +++++++++++++++++++++++++++++++++++ 4 files changed, 425 insertions(+) create mode 100644 host/cmdline/coreclrutil.cpp create mode 100644 host/cmdline/coreclrutil.h create mode 100644 host/cmdline/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f23a2b711..98053e3cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/host/cmdline/coreclrutil.cpp b/host/cmdline/coreclrutil.cpp new file mode 100644 index 000000000..d67a1057f --- /dev/null +++ b/host/cmdline/coreclrutil.cpp @@ -0,0 +1,163 @@ +#include "coreclrutil.h" +#include +#include +#include +#include +#include + +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 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 + diff --git a/host/cmdline/coreclrutil.h b/host/cmdline/coreclrutil.h new file mode 100644 index 000000000..49a45ab6e --- /dev/null +++ b/host/cmdline/coreclrutil.h @@ -0,0 +1,63 @@ +#pragma once + +#include + +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 + diff --git a/host/cmdline/main.cpp b/host/cmdline/main.cpp new file mode 100644 index 000000000..29fbfb938 --- /dev/null +++ b/host/cmdline/main.cpp @@ -0,0 +1,194 @@ +#include +#include +#include "coreclrutil.h" +#include +#include + +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; +} + From ba45c8f07600158c9439de2bd93e675bad0588df Mon Sep 17 00:00:00 2001 From: Peter Honeder Date: Mon, 27 Jul 2015 18:40:33 +0200 Subject: [PATCH 5/9] renamed function pointer type, changed code to call UnmanagedMain --- host/cmdline/main.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/host/cmdline/main.cpp b/host/cmdline/main.cpp index 29fbfb938..485ba56e9 100644 --- a/host/cmdline/main.cpp +++ b/host/cmdline/main.cpp @@ -172,22 +172,23 @@ int main(int argc, char** argv) std::cout << "returnValue=" << returnValue << std::endl; - typedef void (*UnmanagedEntry)(); - UnmanagedEntry unmanagedEntry = nullptr; + typedef void (*UnmanagedMain)(int argc, const char** argv); + UnmanagedMain unmanagedMain = nullptr; status = createDelegate( hostHandle, domainId, "powershell-simple, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "ps_hello_world.Program", - "UnmanagedEntry", - (void**)&unmanagedEntry); + "UnmanagedMain", + (void**)&unmanagedMain); if (0 > status) { - std::cerr << "could not create delegate for UnmanagedEntry - status: " << std::hex << status << std::endl; + std::cerr << "could not create delegate for UnmanagedMain - status: " << std::hex << status << std::endl; return 4; } - unmanagedEntry(); + const char* testargs[] = { "get-process | out-host", "get-module" }; + unmanagedMain(2,testargs); return 0; } From c0414f8fc460f2acca28675310ae3ff63f8903c2 Mon Sep 17 00:00:00 2001 From: Peter Honeder Date: Tue, 28 Jul 2015 18:28:09 +0200 Subject: [PATCH 6/9] cleaned up implementation, added command line parsing and standardized managed interface for cmdline hosts --- CMakeLists.txt | 2 +- host/cmdline/main.cpp | 339 +++++++++++++++++++++++++++++++++--------- 2 files changed, 267 insertions(+), 74 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 98053e3cc..3a7c35b54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ 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) +target_link_libraries(host_cmdline dl icuuc) diff --git a/host/cmdline/main.cpp b/host/cmdline/main.cpp index 485ba56e9..678d8ccc3 100644 --- a/host/cmdline/main.cpp +++ b/host/cmdline/main.cpp @@ -3,62 +3,172 @@ #include "coreclrutil.h" #include #include +#include +#include +#include +#include + +namespace Cmdline +{ + +void printHelp() +{ + std::cerr << "PS CoreCLR host" << std::endl; + std::cerr << "Usage: host_cmdline [-c coreclr_path] [-alc load_context_assembly] [-s search_paths]" << std::endl; + std::cerr << " [-b base_path] assembly_name type_name function_name [...]" << std::endl; + std::cerr << std::endl; + std::cerr << "What it does:" << std::endl; + std::cerr << "- by default the host assumes that CoreCLR is located in the same folder" << std::endl; + std::cerr << " as host_cmdline" << std::endl; + std::cerr << " + this behavior can be overridden with the -c command line argument" << std::endl; + std::cerr << "- by default the host assumes that the assembly named" << std::endl; + std::cerr << " Microsoft.PowerShell.CoreCLR.AssemblyLoadContext is part of the" << std::endl; + std::cerr << " platform assemblies" << std::endl; + std::cerr << " + a custom assembly containing the PowerShellAssemblyLoadContext can" << std::endl; + std::cerr << " be provided with the -alc command line argument" << std::endl; + std::cerr << "- all additional parameters at the end of the command line are forwarded" << std::endl; + std::cerr << " to the specified entry function in the assembly" << std::endl; + std::cerr << "- the host will execute the specified entry function in the specified assembly" << std::endl; + std::cerr << " + this assembly has to be located in the search path" << std::endl; + std::cerr << "- by default the host will add the current working directory to the assembly search path" << std::endl; + std::cerr << " + this can be overridden with the -s command line argument" << std::endl; + std::cerr << "- by default the host assumes the PS base path for the assembly load context is the current" << std::endl; + std::cerr << " working directory" << std::endl; + std::cerr << " + this can be overridden with the -b command line argument" << std::endl; + std::cerr << "- the function signature of the function that gets executed must be:" << std::endl; + std::cerr << " public static int UnmanagedMain(int argc, [MarshalAs(UnmanagedType.LPArray,ArraySubType=UnmanagedType.LPStr,SizeParamIndex=0)] String[] argv)" << std::endl; + std::cerr << std::endl; + std::cerr << "Options:" << std::endl; + std::cerr << "-c, --clr-path path to libcoreclr.so and the managed CLR assemblies" << std::endl; + std::cerr << "-alc path to a dll containing Microsoft.PowerShell.CoreCLR.AssemblyLoadContext" << std::endl; + std::cerr << "-s a list of assembly search paths, separated by :" << std::endl; + std::cerr << "-b the powershell assembly base path" << std::endl; + std::cerr << "assembly_name the assembly name of the assembly to execute" << std::endl; + std::cerr << " must be available in the search path" << std::endl; + std::cerr << "type_name the type name where the function can be found" << std::endl; + std::cerr << "function_name the function to execute (must have the function signature described above!)" << std::endl; + std::cerr << std::endl; + std::cerr << "Example:" << std::endl; + std::cerr << "./host_cmdline -c /test/coreclr -alc /test/ps/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll -s /test/ps -b /test/ps 'powershell-simple, version=1.0.0.0, culture=neutral, PublicKeyToken=null' 'ps_hello_world.Program' 'UnmanagedMain' 'get-process'" << std::endl; +} + +struct Args +{ + Args() : + argc(0), + argv(nullptr) + { + } + + std::string clrPath; + std::string assemblyLoadContextFilePath; + std::string searchPaths; + std::string basePath; + std::string entryAssemblyName; + std::string entryTypeName; + std::string entryFunctionName; + int argc; + char** argv; + + void debugPrint() const + { + std::cerr << "Args:" << std::endl; + std::cerr << "- clrPath " << clrPath << std::endl; + std::cerr << "- assemblyLoadContextFilePath " << assemblyLoadContextFilePath << std::endl; + std::cerr << "- searchPaths " << searchPaths << std::endl; + std::cerr << "- basePath " << basePath << std::endl; + std::cerr << "- entryAssemblyName " << entryAssemblyName << std::endl; + std::cerr << "- entryTypeName " << entryTypeName << std::endl; + std::cerr << "- entryFunctionName " << entryFunctionName << std::endl; + std::cerr << "- argc " << argc << std::endl; + } +}; + +// this is implemented without any 3rd party lib to keep the list +// of dependencies low +bool parseCmdline(const int argc, char** argv, Args& args) +{ + if (argc <= 1) + { + std::cerr << "error: missing arguments" << std::endl; + return false; + } + + for (int i = 1; i < argc; ++i) + { + const std::string arg = argv[i]; + const bool hasNextArg = i+1 < argc; + const std::string nextArg = hasNextArg ? std::string(argv[i+1]) : std::string(""); + + if (hasNextArg && (arg == "-c" || arg == "--clr-path")) + { + args.clrPath = nextArg; + ++i; + } + else if (hasNextArg && arg == "-alc") + { + args.assemblyLoadContextFilePath = nextArg; + ++i; + } + else if (hasNextArg && arg == "-s") + { + args.searchPaths = nextArg; + ++i; + } + else if (hasNextArg && arg == "-b") + { + args.basePath = nextArg; + ++i; + } + else if (args.entryAssemblyName == "") + { + args.entryAssemblyName = arg; + } + else if (args.entryTypeName == "") + { + args.entryTypeName = arg; + } + else if (args.entryFunctionName == "") + { + args.entryFunctionName = arg; + } + else + { + // forward command line parameters + args.argc = argc-i; + args.argv = &argv[i]; + } + } + + // check for mandatory parameters + if (args.entryAssemblyName == "") + { + std::cerr << "error: assembly_name argument missing" << std::endl; + } + if (args.entryTypeName == "") + { + std::cerr << "error: type_name argument missing" << std::endl; + } + if (args.entryFunctionName == "") + { + std::cerr << "error: function_name argument missing" << std::endl; + } + + return true; +} + +} 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) + // parse the command line arguments + Cmdline::Args args; + if (!Cmdline::parseCmdline(argc,argv,args)) { - std::cerr << "Absolute path to CoreCLR library too long" << std::endl; + Cmdline::printHelp(); 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; + args.debugPrint(); // get the absolute path of the current executable std::string currentExeAbsolutePath; @@ -67,6 +177,91 @@ int main(int argc, char** argv) std::cerr << "could not get absolute path of current executable" << std::endl; return 1; } + std::cerr << "currentExeAbsolutePath=" << currentExeAbsolutePath << std::endl; + + // CLR absolute folder path + // + // This path is created from the location of this executable or a path + // specified with the -c command line argument + std::string clrAbsolutePath; + const char* clrPathArg = args.clrPath == "" ? nullptr : args.clrPath.c_str(); + if (!CoreCLRUtil::GetClrFilesAbsolutePath(currentExeAbsolutePath.c_str(),clrPathArg,clrAbsolutePath)) + { + std::cerr << "could not find absolute CLR path" << std::endl; + return 1; + } + + // the path to the CoreCLR library + // + // This is typically libcoreclr.so on Linux and libcoreclr.dylib on Mac + + 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::cerr << "coreClrDllPath: " << coreClrDllPath << std::endl; + + // TPA list + // + // The list of platform assemblies must include all CoreCLR assemblies + // and the Microsoft.PowerShell.CoreCLR.AssemblyLoadContext + // + // if the -alc parameter was specified, add it to the TPA list here + + std::string tpaList; + CoreCLRUtil::AddFilesFromDirectoryToTpaList(clrAbsolutePath.c_str(),tpaList); + + if (args.assemblyLoadContextFilePath != "") + tpaList += ":" + args.assemblyLoadContextFilePath; + + std::cerr << "tpaList: " << tpaList << std::endl; + + // get the absolute path of the current directory + + std::string currentDirAbsolutePath; + if (!CoreCLRUtil::GetAbsolutePath(".",currentDirAbsolutePath)) + { + std::cerr << "failed to get the absolute path from current working directory" << std::endl; + return 1; + } + + // assembly search paths + // + // add the current directory and anything specified with the -s option + + std::string appPath = currentDirAbsolutePath; + if (args.searchPaths != "") + appPath += ":" + args.searchPaths; + std::cerr << "appPath: " << appPath << std::endl; + + // search paths for native dlls + // + // Add both the CoreCLR directory and the regular search paths to this list + std::string nativeDllSearchDirs = appPath + ":" + clrAbsolutePath; + + // convert the app base to utf-16 + // + // this is needed as a utf-16 LE string by CoreCLR/PS's assembly load context interface + // it is either: + // - the current dir's absolute path + // - the path specified through the -b argument + std::string psBasePath = currentDirAbsolutePath; + if (args.basePath != "") + { + if (!CoreCLRUtil::GetAbsolutePath(args.basePath.c_str(),psBasePath)) + { + std::cerr << "failed to get the absolute path from the base_path argument" << std::endl; + return 1; + } + } + std::basic_string psBasePath16(PATH_MAX,0); + + UnicodeString u8str = UnicodeString(psBasePath.c_str(),"UTF-8"); + int32_t targetSize = u8str.extract(0,u8str.length(),(char*)&psBasePath16[0],psBasePath16.size()*sizeof(char16_t),"UTF-16LE"); + psBasePath16.resize(targetSize/sizeof(char16_t)+1); + std::cerr << "targetSize=" << targetSize << std::endl; // open the shared library void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW|RTLD_LOCAL); @@ -155,31 +350,17 @@ int main(int argc, char** argv) std::cerr << "could not create delegate for SetPowerShellAssemblyLoadContext - status: " << std::hex << status << std::endl; return 4; } + loaderDelegate(psBasePath16.c_str()); - 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 (*UnmanagedMain)(int argc, const char** argv); + // call the unmanaged entry point for PowerShell + typedef int (*UnmanagedMain)(int argc, char const* const* argv); UnmanagedMain unmanagedMain = nullptr; status = createDelegate( hostHandle, domainId, - "powershell-simple, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", - "ps_hello_world.Program", - "UnmanagedMain", + args.entryAssemblyName.c_str(), + args.entryTypeName.c_str(), + args.entryFunctionName.c_str(), (void**)&unmanagedMain); if (0 > status) { @@ -187,9 +368,21 @@ int main(int argc, char** argv) return 4; } - const char* testargs[] = { "get-process | out-host", "get-module" }; - unmanagedMain(2,testargs); + int exitCode = unmanagedMain(args.argc,args.argv); - return 0; + // shutdown CoreCLR + status = shutdownCoreCLR(hostHandle,domainId); + if (0 > status) + { + std::cerr << "coreclr_shutdown failed - status: " << std::hex << status << std::endl; + } + + // close the dynamic library + if (0 != dlclose(coreclrLib)) + { + std::cerr << "failed to close CoreCLR library" << std::endl; + } + + return exitCode; } From ea76b7e8fdc269ffd8f571efa88930010b57d75c Mon Sep 17 00:00:00 2001 From: Peter Honeder Date: Tue, 28 Jul 2015 18:49:32 +0200 Subject: [PATCH 7/9] fixed string termination issue, added verbose mode --- host/cmdline/main.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/host/cmdline/main.cpp b/host/cmdline/main.cpp index 678d8ccc3..11c933367 100644 --- a/host/cmdline/main.cpp +++ b/host/cmdline/main.cpp @@ -43,6 +43,7 @@ void printHelp() std::cerr << "-alc path to a dll containing Microsoft.PowerShell.CoreCLR.AssemblyLoadContext" << std::endl; std::cerr << "-s a list of assembly search paths, separated by :" << std::endl; std::cerr << "-b the powershell assembly base path" << std::endl; + std::cerr << "-v verbose output, show paths" << std::endl; std::cerr << "assembly_name the assembly name of the assembly to execute" << std::endl; std::cerr << " must be available in the search path" << std::endl; std::cerr << "type_name the type name where the function can be found" << std::endl; @@ -56,7 +57,8 @@ struct Args { Args() : argc(0), - argv(nullptr) + argv(nullptr), + verbose(false) { } @@ -69,6 +71,7 @@ struct Args std::string entryFunctionName; int argc; char** argv; + bool verbose; void debugPrint() const { @@ -81,6 +84,7 @@ struct Args std::cerr << "- entryTypeName " << entryTypeName << std::endl; std::cerr << "- entryFunctionName " << entryFunctionName << std::endl; std::cerr << "- argc " << argc << std::endl; + std::cerr << "- verbose " << (verbose ? "true" : "false") << std::endl; } }; @@ -168,7 +172,8 @@ int main(int argc, char** argv) Cmdline::printHelp(); return 1; } - args.debugPrint(); + if (args.verbose) + args.debugPrint(); // get the absolute path of the current executable std::string currentExeAbsolutePath; @@ -177,7 +182,8 @@ int main(int argc, char** argv) std::cerr << "could not get absolute path of current executable" << std::endl; return 1; } - std::cerr << "currentExeAbsolutePath=" << currentExeAbsolutePath << std::endl; + if (args.verbose) + std::cerr << "currentExeAbsolutePath=" << currentExeAbsolutePath << std::endl; // CLR absolute folder path // @@ -201,7 +207,8 @@ int main(int argc, char** argv) std::cerr << "Absolute path to CoreCLR library too long" << std::endl; return 1; } - std::cerr << "coreClrDllPath: " << coreClrDllPath << std::endl; + if (args.verbose) + std::cerr << "coreClrDllPath: " << coreClrDllPath << std::endl; // TPA list // @@ -216,7 +223,8 @@ int main(int argc, char** argv) if (args.assemblyLoadContextFilePath != "") tpaList += ":" + args.assemblyLoadContextFilePath; - std::cerr << "tpaList: " << tpaList << std::endl; + if (args.verbose) + std::cerr << "tpaList: " << tpaList << std::endl; // get the absolute path of the current directory @@ -234,7 +242,9 @@ int main(int argc, char** argv) std::string appPath = currentDirAbsolutePath; if (args.searchPaths != "") appPath += ":" + args.searchPaths; - std::cerr << "appPath: " << appPath << std::endl; + + if (args.verbose) + std::cerr << "appPath: " << appPath << std::endl; // search paths for native dlls // @@ -256,12 +266,13 @@ int main(int argc, char** argv) return 1; } } - std::basic_string psBasePath16(PATH_MAX,0); + + // make sure to leave 1 byte at the end for null termination + std::basic_string psBasePath16(PATH_MAX+1,0); UnicodeString u8str = UnicodeString(psBasePath.c_str(),"UTF-8"); - int32_t targetSize = u8str.extract(0,u8str.length(),(char*)&psBasePath16[0],psBasePath16.size()*sizeof(char16_t),"UTF-16LE"); + int32_t targetSize = u8str.extract(0,u8str.length(),(char*)&psBasePath16[0],(psBasePath16.size()-1)*sizeof(char16_t),"UTF-16LE"); psBasePath16.resize(targetSize/sizeof(char16_t)+1); - std::cerr << "targetSize=" << targetSize << std::endl; // open the shared library void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW|RTLD_LOCAL); From ff2ceeceb758cba907a18ab3bd7132cf36bc15e8 Mon Sep 17 00:00:00 2001 From: Peter Honeder Date: Wed, 29 Jul 2015 18:12:31 +0200 Subject: [PATCH 8/9] added interpreting the verbose option --- host/cmdline/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/host/cmdline/main.cpp b/host/cmdline/main.cpp index 11c933367..7793bf143 100644 --- a/host/cmdline/main.cpp +++ b/host/cmdline/main.cpp @@ -124,6 +124,10 @@ bool parseCmdline(const int argc, char** argv, Args& args) args.basePath = nextArg; ++i; } + else if (arg == "-v") + { + args.verbose = true; + } else if (args.entryAssemblyName == "") { args.entryAssemblyName = arg; From 8eeb24ece919d11fcfc41144d27743a97c0b0791 Mon Sep 17 00:00:00 2001 From: Peter Honeder Date: Thu, 30 Jul 2015 16:53:35 +0200 Subject: [PATCH 9/9] added hostutil.h, added tests for hostutil, added more cmdline options to PS host --- CMakeLists.txt | 9 +++- host/cmdline/main.cpp | 53 +++++++++++++++++++++--- host/{cmdline => common}/coreclrutil.cpp | 4 ++ host/{cmdline => common}/coreclrutil.h | 0 host/common/hostutil.h | 48 +++++++++++++++++++++ tests/host/test-hostutil.cpp | 35 ++++++++++++++++ 6 files changed, 142 insertions(+), 7 deletions(-) rename host/{cmdline => common}/coreclrutil.cpp (96%) rename host/{cmdline => common}/coreclrutil.h (100%) create mode 100644 host/common/hostutil.h create mode 100644 tests/host/test-hostutil.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a7c35b54..a5d27e7bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,9 +9,11 @@ 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(HOST_COMMON_SOURCE_FILES host/common/coreclrutil.cpp) +set(HOST_COMMON_TEST_SOURCE_FILES tests/host/test-hostutil.cpp) +set(TEST_SOURCE_FILES tests/test-getcurrentprocessid.cpp ${HOST_COMMON_SOURCE_FILES} ${HOST_COMMON_TEST_SOURCE_FILES}) 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) +SET(HOST_CMDLINE_SOURCE_FILES host/cmdline/main.cpp ${HOST_COMMON_SOURCE_FILES}) # target definitions add_library(ps SHARED ${LIB_SOURCE_FILES}) @@ -23,5 +25,8 @@ find_package(Threads) target_link_libraries(monad_native ${CMAKE_THREAD_LIBS_INIT} ps) target_link_libraries(host_cmdline dl icuuc) +# target specific include directories +target_include_directories(monad_native PRIVATE host) +target_include_directories(host_cmdline PRIVATE host) diff --git a/host/cmdline/main.cpp b/host/cmdline/main.cpp index 7793bf143..740d99185 100644 --- a/host/cmdline/main.cpp +++ b/host/cmdline/main.cpp @@ -1,6 +1,7 @@ #include #include -#include "coreclrutil.h" +#include "common/coreclrutil.h" +#include "common/hostutil.h" #include #include #include @@ -32,6 +33,7 @@ void printHelp() std::cerr << " + this assembly has to be located in the search path" << std::endl; std::cerr << "- by default the host will add the current working directory to the assembly search path" << std::endl; std::cerr << " + this can be overridden with the -s command line argument" << std::endl; + std::cerr << " + if -c is specified, it will be added to the search path instead of the current directory" << std::endl; std::cerr << "- by default the host assumes the PS base path for the assembly load context is the current" << std::endl; std::cerr << " working directory" << std::endl; std::cerr << " + this can be overridden with the -b command line argument" << std::endl; @@ -44,13 +46,17 @@ void printHelp() std::cerr << "-s a list of assembly search paths, separated by :" << std::endl; std::cerr << "-b the powershell assembly base path" << std::endl; std::cerr << "-v verbose output, show paths" << std::endl; + std::cerr << "-tpa additional list of trusted platform assemblies, this references dll and exe files" << std::endl; + std::cerr << " separated by :" << std::endl; + std::cerr << " unless part of the same folder as CoreCLR, the main assembly referenced with the assembly_name" << std::endl; + std::cerr << " argument, must always be added to the TPA list with this parameter" << std::endl; std::cerr << "assembly_name the assembly name of the assembly to execute" << std::endl; std::cerr << " must be available in the search path" << std::endl; std::cerr << "type_name the type name where the function can be found" << std::endl; std::cerr << "function_name the function to execute (must have the function signature described above!)" << std::endl; std::cerr << std::endl; std::cerr << "Example:" << std::endl; - std::cerr << "./host_cmdline -c /test/coreclr -alc /test/ps/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll -s /test/ps -b /test/ps 'powershell-simple, version=1.0.0.0, culture=neutral, PublicKeyToken=null' 'ps_hello_world.Program' 'UnmanagedMain' 'get-process'" << std::endl; + std::cerr << "./host_cmdline -c /test/coreclr -alc /test/ps/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll -s /test/ps -b /test/ps -tpa /test/ps/powershell-simple.exe 'powershell-simple, version=1.0.0.0, culture=neutral, PublicKeyToken=null' 'ps_hello_world.Program' 'UnmanagedMain' 'get-process'" << std::endl; } struct Args @@ -66,6 +72,7 @@ struct Args std::string assemblyLoadContextFilePath; std::string searchPaths; std::string basePath; + std::string tpaList; std::string entryAssemblyName; std::string entryTypeName; std::string entryFunctionName; @@ -80,6 +87,7 @@ struct Args std::cerr << "- assemblyLoadContextFilePath " << assemblyLoadContextFilePath << std::endl; std::cerr << "- searchPaths " << searchPaths << std::endl; std::cerr << "- basePath " << basePath << std::endl; + std::cerr << "- tpaList " << tpaList << std::endl; std::cerr << "- entryAssemblyName " << entryAssemblyName << std::endl; std::cerr << "- entryTypeName " << entryTypeName << std::endl; std::cerr << "- entryFunctionName " << entryFunctionName << std::endl; @@ -124,6 +132,11 @@ bool parseCmdline(const int argc, char** argv, Args& args) args.basePath = nextArg; ++i; } + else if (hasNextArg && arg == "-tpa") + { + args.tpaList = nextArg; + ++i; + } else if (arg == "-v") { args.verbose = true; @@ -145,6 +158,9 @@ bool parseCmdline(const int argc, char** argv, Args& args) // forward command line parameters args.argc = argc-i; args.argv = &argv[i]; + + // explicitly break here because the lines above consume all remaining arguments + break; } } @@ -200,6 +216,8 @@ int main(int argc, char** argv) std::cerr << "could not find absolute CLR path" << std::endl; return 1; } + if (args.verbose) + std::cerr << "clrAbsolutePath=" << clrAbsolutePath << std::endl; // the path to the CoreCLR library // @@ -225,7 +243,23 @@ int main(int argc, char** argv) CoreCLRUtil::AddFilesFromDirectoryToTpaList(clrAbsolutePath.c_str(),tpaList); if (args.assemblyLoadContextFilePath != "") - tpaList += ":" + args.assemblyLoadContextFilePath; + { + std::string assemblyLoadContextAbsoluteFilePath; + if (!CoreCLRUtil::GetAbsolutePath(args.assemblyLoadContextFilePath.c_str(),assemblyLoadContextAbsoluteFilePath)) + { + std::cerr << "Failed to get absolute file path for assembly load context" << std::endl; + return 1; + } + tpaList += ":" + assemblyLoadContextAbsoluteFilePath; + } + + // add the -tpa command line argument + if (args.tpaList != "") + { + std::string tpaAbsolutePathList = HostUtil::getAbsolutePathList(args.tpaList); + if (tpaAbsolutePathList != "") + tpaList += ":" + tpaAbsolutePathList; + } if (args.verbose) std::cerr << "tpaList: " << tpaList << std::endl; @@ -241,11 +275,18 @@ int main(int argc, char** argv) // assembly search paths // - // add the current directory and anything specified with the -s option + // add the current directory, and optionally the CoreCLR directory if -c was specified + // and anything specified with the -s option std::string appPath = currentDirAbsolutePath; + if (args.clrPath != "") + appPath += ":" + clrAbsolutePath; if (args.searchPaths != "") - appPath += ":" + args.searchPaths; + { + std::string searchAbsolutePathList = HostUtil::getAbsolutePathList(args.searchPaths); + if (searchAbsolutePathList != "") + appPath += ":" + searchAbsolutePathList; + } if (args.verbose) std::cerr << "appPath: " << appPath << std::endl; @@ -270,6 +311,8 @@ int main(int argc, char** argv) return 1; } } + if (args.verbose) + std::cerr << "psBasePath=" << psBasePath << std::endl; // make sure to leave 1 byte at the end for null termination std::basic_string psBasePath16(PATH_MAX+1,0); diff --git a/host/cmdline/coreclrutil.cpp b/host/common/coreclrutil.cpp similarity index 96% rename from host/cmdline/coreclrutil.cpp rename to host/common/coreclrutil.cpp index d67a1057f..6aa8b9896 100644 --- a/host/cmdline/coreclrutil.cpp +++ b/host/common/coreclrutil.cpp @@ -157,6 +157,10 @@ void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList) } closedir(dir); + + // strip any trailing : from the tpaList + if (tpaList.size() > 0 && tpaList[tpaList.size()-1] == ':') + tpaList.resize(tpaList.size()-1); } } // namespace CoreCLRUtil diff --git a/host/cmdline/coreclrutil.h b/host/common/coreclrutil.h similarity index 100% rename from host/cmdline/coreclrutil.h rename to host/common/coreclrutil.h diff --git a/host/common/hostutil.h b/host/common/hostutil.h new file mode 100644 index 000000000..a8c9a208d --- /dev/null +++ b/host/common/hostutil.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include "common/coreclrutil.h" + +namespace HostUtil +{ + +//!\brief get a list of absolute paths separated by : from a list of relative/absolute paths separated by : +std::string getAbsolutePathList(const std::string& paths) +{ + //std::cerr << "getAbsolutePathList: paths=" << paths << std::endl; + std::string result; + + // split by : + size_t lastPos = 0; + size_t curPos = paths.find(':',lastPos); + do + { + const std::string token = paths.substr(lastPos,curPos-lastPos); + //std::cerr << "curPos=" << curPos << " lastPos=" << lastPos << " token=" << token << std::endl; + + // skip empty tokens + if (token != "") + { + std::string absolutePath; + if (CoreCLRUtil::GetAbsolutePath(token.c_str(),absolutePath)) + { + // add colons correctly + if (result.size() == 0) + result += absolutePath; + else + result += ":" + absolutePath; + } + } + + // increment lastPos to skip the : + lastPos += token.size() + 1; + curPos = paths.find(':',lastPos); + } + while (lastPos < paths.size()); + + return result; +} + +} + + diff --git a/tests/host/test-hostutil.cpp b/tests/host/test-hostutil.cpp new file mode 100644 index 000000000..7a8b6b473 --- /dev/null +++ b/tests/host/test-hostutil.cpp @@ -0,0 +1,35 @@ +#include +#include "common/hostutil.h" + +TEST(HostUtilTest,simple) +{ + // syntactical corner cases + ASSERT_EQ("",HostUtil::getAbsolutePathList("")); + ASSERT_EQ("",HostUtil::getAbsolutePathList(":")); + ASSERT_EQ("",HostUtil::getAbsolutePathList("::")); + ASSERT_EQ("",HostUtil::getAbsolutePathList(":::::")); + + // current directory + char* cwd = get_current_dir_name(); + ASSERT_EQ(std::string(cwd),HostUtil::getAbsolutePathList(".")); + + // relative and absolute paths that don't exist + ASSERT_EQ("",HostUtil::getAbsolutePathList("/something/that/does/not/exist")); + ASSERT_EQ("",HostUtil::getAbsolutePathList(":/something/that/does/not/exist:")); + ASSERT_EQ("",HostUtil::getAbsolutePathList("something/relative/that/does/not/exist")); + ASSERT_EQ("",HostUtil::getAbsolutePathList(":something/relative/that/does/not/exist:")); + + // absolute existing paths + ASSERT_EQ("/tmp",HostUtil::getAbsolutePathList("/tmp")); + ASSERT_EQ("/tmp:/tmp",HostUtil::getAbsolutePathList("/tmp:/tmp")); + + // relative paths + chdir("/"); + ASSERT_EQ("/tmp",HostUtil::getAbsolutePathList("tmp")); + ASSERT_EQ("/tmp:/tmp",HostUtil::getAbsolutePathList("/tmp:tmp:")); + chdir(cwd); + + // cleanup + free(cwd); +} +