From 441a97709757ec1b6edcaadb733941e64af8fb60 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 15 Jul 2015 23:08:09 +0200 Subject: [PATCH] Extend the Unix hosting API 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 --- CMakeLists.txt | 2 +- generateexportedsymbols.awk | 7 +- generateversionscript.awk | 7 +- .../hosts/unixcoreruncommon/coreruncommon.cpp | 121 ++++++---- src/dlls/mscoree/mscorwks_unixexports.src | 33 ++- src/dlls/mscoree/unixinterface.cpp | 215 ++++++++++++++---- src/dlls/mscorrc/CMakeLists.txt | 2 +- 7 files changed, 292 insertions(+), 95 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f7872e00ee3f..81fd930488fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,7 +121,7 @@ function(generate_exports_file inputFilename outputFilename) add_custom_command( OUTPUT ${outputFilename} COMMAND ${AWK} -f ${CMAKE_SOURCE_DIR}/${AWK_SCRIPT} ${inputFilename} >${outputFilename} - DEPENDS ${inputFilename} + DEPENDS ${inputFilename} ${CMAKE_SOURCE_DIR}/${AWK_SCRIPT} COMMENT "Generating exports file ${outputFilename}" ) set_source_files_properties(${outputFilename} diff --git a/generateexportedsymbols.awk b/generateexportedsymbols.awk index 19312c381eab..1d9b200d2361 100644 --- a/generateexportedsymbols.awk +++ b/generateexportedsymbols.awk @@ -2,5 +2,10 @@ # Remove the CR character in case the sources are mapped from # a Windows share and contain CRLF line endings gsub(/\r/,"", $0); - print "_" $0; + + # Skip empty lines and comment lines starting with semicolon + if (NF && !match($0, /^[:space:]*;/)) + { + print "_" $0; + } } diff --git a/generateversionscript.awk b/generateversionscript.awk index 2aa1f4f0a2f9..d1c294be5818 100644 --- a/generateversionscript.awk +++ b/generateversionscript.awk @@ -6,7 +6,12 @@ BEGIN { # Remove the CR character in case the sources are mapped from # a Windows share and contain CRLF line endings gsub(/\r/,"", $0); - print " " $0 ";"; + + # Skip empty lines and comment lines starting with semicolon + if (NF && !match($0, /^[:space:]*;/)) + { + print " " $0 ";"; + } } END { print " local: *;" diff --git a/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp index 4f6ffa033082..ecbbf392903c 100644 --- a/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp +++ b/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp @@ -24,29 +24,31 @@ static const char * const coreClrDll = "libcoreclr.dylib"; static const char * const coreClrDll = "libcoreclr.so"; #endif -// Windows types used by the ExecuteAssembly function -typedef unsigned int DWORD; -typedef const char16_t* LPCWSTR; -typedef const char* LPCSTR; -typedef int32_t HRESULT; - -#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) - -// Prototype of the ExecuteAssembly function from the libcoreclr.do -typedef HRESULT (*ExecuteAssemblyFunction)( - LPCSTR exePath, - LPCSTR coreClrPath, - LPCSTR appDomainFriendlyName, - int propertyCount, - LPCSTR* propertyKeys, - LPCSTR* propertyValues, - int argc, - LPCSTR* argv, - LPCSTR managedAssemblyPath, - LPCSTR entryPointAssemblyName, - LPCSTR entryPointTypeName, - LPCSTR entryPointMethodsName, - DWORD* exitCode); +#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) { @@ -233,8 +235,23 @@ int ExecuteManagedAssembly( void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW | RTLD_LOCAL); if (coreclrLib != nullptr) { - ExecuteAssemblyFunction executeAssembly = (ExecuteAssemblyFunction)dlsym(coreclrLib, "ExecuteAssembly"); - if (executeAssembly != 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 @@ -272,30 +289,46 @@ int ExecuteManagedAssembly( "UseLatestBehaviorWhenTFMNotSpecified" }; - HRESULT st = executeAssembly( - currentExeAbsolutePath, - coreClrDllPath.c_str(), - "unixcorerun", - sizeof(propertyKeys) / sizeof(propertyKeys[0]), - propertyKeys, - propertyValues, - managedAssemblyArgc, - managedAssemblyArgv, - managedAssemblyAbsolutePath, - NULL, - NULL, - NULL, - (DWORD*)&exitCode); + void* hostHandle; + unsigned int domainId; + + int st = initializeCoreCLR( + currentExeAbsolutePath, + "unixcorerun", + sizeof(propertyKeys) / sizeof(propertyKeys[0]), + propertyKeys, + propertyValues, + &hostHandle, + &domainId); if (!SUCCEEDED(st)) { - fprintf(stderr, "ExecuteAssembly failed - status: 0x%08x\n", st); + fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st); exitCode = -1; } - } - else - { - fprintf(stderr, "Function ExecuteAssembly not found in the libcoreclr.so\n"); + 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) diff --git a/src/dlls/mscoree/mscorwks_unixexports.src b/src/dlls/mscoree/mscorwks_unixexports.src index 81a99d6a7ed2..e96b25a66462 100644 --- a/src/dlls/mscoree/mscorwks_unixexports.src +++ b/src/dlls/mscoree/mscorwks_unixexports.src @@ -1,7 +1,30 @@ +; Unix hosting API +coreclr_create_delegate +coreclr_execute_assembly +coreclr_initialize +coreclr_shutdown + +; Obsolete Unix hosting API, to be removed +ExecuteAssembly + +; PAL initialization and module registration +PAL_InitializeCoreCLR +PAL_RegisterModule +PAL_UnregisterModule + +; Functions exported by the coreclr +CoreDllMain +DllMain +GetCLRRuntimeHost + +; Functions used by CoreFX +EnsureOpenSslInitialized +ForkAndExecProcess + +; Win32 API and other PAL functions used by the mscorlib CloseHandle CoCreateGuid CopyFileW -CoreDllMain CoTaskMemAlloc CoTaskMemFree CreateDirectoryW @@ -10,19 +33,14 @@ CreateFileW CreateMutexW CreateSemaphoreW DeleteFileW -DllMain DuplicateHandle -EnsureOpenSslInitialized -ExecuteAssembly FindClose FindFirstFileW FindNextFileW FlushFileBuffers -ForkAndExecProcess FormatMessageW FreeEnvironmentStringsW GetACP -GetCLRRuntimeHost GetConsoleCP GetConsoleOutputCP GetCurrentDirectoryW @@ -53,10 +71,7 @@ MultiByteToWideChar OpenEventW OpenMutexW OpenSemaphoreW -PAL_InitializeCoreCLR PAL_Random -PAL_RegisterModule -PAL_UnregisterModule QueryPerformanceCounter QueryPerformanceFrequency RaiseException diff --git a/src/dlls/mscoree/unixinterface.cpp b/src/dlls/mscoree/unixinterface.cpp index 21419173e957..5c5e1dacc715 100644 --- a/src/dlls/mscoree/unixinterface.cpp +++ b/src/dlls/mscoree/unixinterface.cpp @@ -88,48 +88,30 @@ static LPCWSTR* StringArrayToUnicode(int argc, LPCSTR* argv) } // -// Execute a managed assembly with given arguments +// Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain // // Parameters: // exePath - Absolute path of the executable that invoked the ExecuteAssembly -// coreClrPath - Absolute path of the libcoreclr.so // appDomainFriendlyName - Friendly name of the app domain that will be created to execute the assembly // propertyCount - Number of properties (elements of the following two arguments) // propertyKeys - Keys of properties of the app domain // propertyValues - Values of properties of the app domain -// argc - Number of arguments passed to the executed assembly -// argv - Array of arguments passed to the executed assembly -// managedAssemblyPath - Path of the managed assembly to execute (or NULL if using a custom entrypoint). -// enntyPointAssemblyName - Name of the assembly which holds the custom entry point (or NULL to use managedAssemblyPath). -// entryPointTypeName - Name of the type which holds the custom entry point (or NULL to use managedAssemblyPath). -// entryPointMethodName - Name of the method which is the custom entry point (or NULL to use managedAssemblyPath). -// exitCode - Exit code returned by the executed assembly +// hostHandle - Output parameter, handle of the created host +// domainId - Output parameter, id of the created app domain // // Returns: // HRESULT indicating status of the operation. S_OK if the assembly was successfully executed // extern "C" -HRESULT ExecuteAssembly( - LPCSTR exePath, - LPCSTR coreClrPath, - LPCSTR appDomainFriendlyName, +int coreclr_initialize( + const char* exePath, + const char* appDomainFriendlyName, int propertyCount, - LPCSTR* propertyKeys, - LPCSTR* propertyValues, - int argc, - LPCSTR* argv, - LPCSTR managedAssemblyPath, - LPCSTR entryPointAssemblyName, - LPCSTR entryPointTypeName, - LPCSTR entryPointMethodName, - DWORD* exitCode) + const char** propertyKeys, + const char** propertyValues, + void** hostHandle, + unsigned int* domainId) { - if (exitCode == NULL) - { - return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); - } - *exitCode = -1; - DWORD error = PAL_InitializeCoreCLR(exePath); HRESULT hr = HRESULT_FROM_WIN32(error); @@ -161,8 +143,6 @@ HRESULT ExecuteAssembly( ConstWStringArrayHolder propertyValuesW; propertyValuesW.Set(StringArrayToUnicode(propertyCount, propertyValues), propertyCount); - DWORD domainId; - hr = host->CreateAppDomainWithManager( appDomainFriendlyNameW, // Flags: @@ -187,7 +167,172 @@ HRESULT ExecuteAssembly( propertyCount, propertyKeysW, propertyValuesW, - &domainId); + domainId); + + if (SUCCEEDED(hr)) + { + host.SuppressRelease(); + *hostHandle = host; + } + + return hr; +} + +// +// Shutdown CoreCLR. It unloads the app domain and stops the CoreCLR host. +// +// Parameters: +// hostHandle - Handle of the host +// domainId - Id of the domain +// +// Returns: +// HRESULT indicating status of the operation. S_OK if the assembly was successfully executed +// +extern "C" +int coreclr_shutdown( + void* hostHandle, + unsigned int domainId) +{ + ReleaseHolder host(reinterpret_cast(hostHandle)); + HRESULT hr = host->UnloadAppDomain(domainId, + true); // Wait until done + IfFailRet(hr); + + hr = host->Stop(); + + // The PAL_Terminate is not called here since it would terminate the current process. + + return hr; +} + +// +// Create a native callable delegate for a managed method. +// +// Parameters: +// hostHandle - Handle of the host +// domainId - Id of the domain +// entryPointAssemblyName - Name of the assembly which holds the custom entry point +// entryPointTypeName - Name of the type which holds the custom entry point +// entryPointMethodName - Name of the method which is the custom entry point +// delegate - Output parameter, the function stores a pointer to the delegate at the specified address +// +// Returns: +// HRESULT indicating status of the operation. S_OK if the assembly was successfully executed +// +extern "C" +int coreclr_create_delegate( + void* hostHandle, + unsigned int domainId, + const char* entryPointAssemblyName, + const char* entryPointTypeName, + const char* entryPointMethodName, + void** delegate) +{ + ICLRRuntimeHost2* host = reinterpret_cast(hostHandle); + + ConstWStringHolder entryPointAssemblyNameW = StringToUnicode(entryPointAssemblyName); + ConstWStringHolder entryPointTypeNameW = StringToUnicode(entryPointTypeName); + ConstWStringHolder entryPointMethodNameW = StringToUnicode(entryPointMethodName); + + HRESULT hr = host->CreateDelegate( + domainId, + entryPointAssemblyNameW, + entryPointTypeNameW, + entryPointMethodNameW, + (INT_PTR*)delegate); + + return hr; +} + +// +// Execute a managed assembly with given arguments +// +// Parameters: +// hostHandle - Handle of the host +// domainId - Id of the domain +// argc - Number of arguments passed to the executed assembly +// argv - Array of arguments passed to the executed assembly +// managedAssemblyPath - Path of the managed assembly to execute (or NULL if using a custom entrypoint). +// exitCode - Exit code returned by the executed assembly +// +// Returns: +// HRESULT indicating status of the operation. S_OK if the assembly was successfully executed +// +extern "C" +int coreclr_execute_assembly( + void* hostHandle, + unsigned int domainId, + int argc, + const char** argv, + const char* managedAssemblyPath, + unsigned int* exitCode) +{ + if (exitCode == NULL) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); + } + *exitCode = -1; + + ICLRRuntimeHost2* host = reinterpret_cast(hostHandle); + + ConstWStringArrayHolder argvW; + argvW.Set(StringArrayToUnicode(argc, argv), argc); + + ConstWStringHolder managedAssemblyPathW = StringToUnicode(managedAssemblyPath); + + HRESULT hr = host->ExecuteAssembly(domainId, managedAssemblyPathW, argc, argvW, exitCode); + IfFailRet(hr); + + return hr; +} + +// +// Execute a managed assembly with given arguments +// +// Parameters: +// exePath - Absolute path of the executable that invoked the ExecuteAssembly +// coreClrPath - Absolute path of the libcoreclr.so +// appDomainFriendlyName - Friendly name of the app domain that will be created to execute the assembly +// propertyCount - Number of properties (elements of the following two arguments) +// propertyKeys - Keys of properties of the app domain +// propertyValues - Values of properties of the app domain +// argc - Number of arguments passed to the executed assembly +// argv - Array of arguments passed to the executed assembly +// managedAssemblyPath - Path of the managed assembly to execute (or NULL if using a custom entrypoint). +// enntyPointAssemblyName - Name of the assembly which holds the custom entry point (or NULL to use managedAssemblyPath). +// entryPointTypeName - Name of the type which holds the custom entry point (or NULL to use managedAssemblyPath). +// entryPointMethodName - Name of the method which is the custom entry point (or NULL to use managedAssemblyPath). +// exitCode - Exit code returned by the executed assembly +// +// Returns: +// HRESULT indicating status of the operation. S_OK if the assembly was successfully executed +// +extern "C" +HRESULT ExecuteAssembly( + LPCSTR exePath, + LPCSTR coreClrPath, + LPCSTR appDomainFriendlyName, + int propertyCount, + LPCSTR* propertyKeys, + LPCSTR* propertyValues, + int argc, + LPCSTR* argv, + LPCSTR managedAssemblyPath, + LPCSTR entryPointAssemblyName, + LPCSTR entryPointTypeName, + LPCSTR entryPointMethodName, + DWORD* exitCode) +{ + if (exitCode == NULL) + { + return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); + } + *exitCode = -1; + + ReleaseHolder host; //(reinterpret_cast(hostHandle)); + DWORD domainId; + + HRESULT hr = coreclr_initialize(exePath, appDomainFriendlyName, propertyCount, propertyKeys, propertyValues, &host, &domainId); IfFailRet(hr); ConstWStringArrayHolder argvW; @@ -219,14 +364,8 @@ HRESULT ExecuteAssembly( *exitCode = pHostMain(argc, argvW); } - - hr = host->UnloadAppDomain(domainId, - true); // Wait until done - IfFailRet(hr); - - hr = host->Stop(); - // The PAL_Terminate is not called here since it would terminate the current process. + hr = coreclr_shutdown(host, domainId); return hr; } diff --git a/src/dlls/mscorrc/CMakeLists.txt b/src/dlls/mscorrc/CMakeLists.txt index 198fe8deb649..e139bc3a30d9 100644 --- a/src/dlls/mscorrc/CMakeLists.txt +++ b/src/dlls/mscorrc/CMakeLists.txt @@ -28,7 +28,7 @@ else() COMMAND ${CMAKE_CXX_COMPILER} -E -P ${PREPROCESS_DEFINITIONS} ${INCLUDE_DIRECTORIES} -o ${PREPROCESSED_SOURCE} -x c ${SOURCE} # Convert the preprocessed .rc file to a C++ file which will be used to make a static lib. COMMAND ${AWK} -f ${RC_TO_CPP} -f ${PROCESS_RC} ${PREPROCESSED_SOURCE} >${RESOURCE_ENTRY_ARRAY_CPP} - DEPENDS ${SOURCE} + DEPENDS ${SOURCE} ${RC_TO_CPP} ${PROCESS_RC} ) include_directories(${RESOURCE_STRING_HEADER_DIR})