// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. //***************************************************************************** // unixinterface.cpp // // Implementation for the interface exposed by libcoreclr.so // //***************************************************************************** #include "stdafx.h" #include #include #include #ifdef FEATURE_GDBJIT #include "../../vm/gdbjithelpers.h" #endif // FEATURE_GDBJIT typedef int (STDMETHODCALLTYPE *HostMain)( const int argc, const wchar_t** argv ); #define ASSERTE_ALL_BUILDS(expr) _ASSERTE_ALL_BUILDS(__FILE__, (expr)) // Holder for const wide strings typedef NewArrayHolder ConstWStringHolder; // Holder for array of wide strings class ConstWStringArrayHolder : public NewArrayHolder { int m_cElements; public: ConstWStringArrayHolder() : NewArrayHolder(), m_cElements(0) { } void Set(LPCWSTR* value, int cElements) { NewArrayHolder::operator=(value); m_cElements = cElements; } ~ConstWStringArrayHolder() { for (int i = 0; i < m_cElements; i++) { delete [] this->m_value[i]; } } }; // Convert 8 bit string to unicode static LPCWSTR StringToUnicode(LPCSTR str) { int length = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); ASSERTE_ALL_BUILDS(length != 0); LPWSTR result = new (nothrow) WCHAR[length]; ASSERTE_ALL_BUILDS(result != NULL); length = MultiByteToWideChar(CP_UTF8, 0, str, -1, result, length); ASSERTE_ALL_BUILDS(length != 0); return result; } // Convert 8 bit string array to unicode string array static LPCWSTR* StringArrayToUnicode(int argc, LPCSTR* argv) { LPCWSTR* argvW = nullptr; if (argc > 0) { argvW = new (nothrow) LPCWSTR[argc]; ASSERTE_ALL_BUILDS(argvW != 0); for (int i = 0; i < argc; i++) { argvW[i] = StringToUnicode(argv[i]); } } return argvW; } static void InitializeStartupFlags(STARTUP_FLAGS* startupFlagsRef) { STARTUP_FLAGS startupFlags = static_cast( STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_FLAGS::STARTUP_SINGLE_APPDOMAIN); if (Configuration::GetKnobBooleanValue(W("System.GC.Concurrent"), CLRConfig::UNSUPPORTED_gcConcurrent)) { startupFlags = static_cast(startupFlags | STARTUP_CONCURRENT_GC); } if (Configuration::GetKnobBooleanValue(W("System.GC.Server"), CLRConfig::UNSUPPORTED_gcServer)) { startupFlags = static_cast(startupFlags | STARTUP_SERVER_GC); } if (Configuration::GetKnobBooleanValue(W("System.GC.RetainVM"), CLRConfig::UNSUPPORTED_GCRetainVM)) { startupFlags = static_cast(startupFlags | STARTUP_HOARD_GC_VM); } *startupFlagsRef = startupFlags; } static void ConvertConfigPropertiesToUnicode( const char** propertyKeys, const char** propertyValues, int propertyCount, LPCWSTR** propertyKeysWRef, LPCWSTR** propertyValuesWRef) { LPCWSTR* propertyKeysW = new (nothrow) LPCWSTR[propertyCount]; ASSERTE_ALL_BUILDS(propertyKeysW != nullptr); LPCWSTR* propertyValuesW = new (nothrow) LPCWSTR[propertyCount]; ASSERTE_ALL_BUILDS(propertyValuesW != nullptr); for (int propertyIndex = 0; propertyIndex < propertyCount; ++propertyIndex) { propertyKeysW[propertyIndex] = StringToUnicode(propertyKeys[propertyIndex]); propertyValuesW[propertyIndex] = StringToUnicode(propertyValues[propertyIndex]); } *propertyKeysWRef = propertyKeysW; *propertyValuesWRef = propertyValuesW; } #if !defined(FEATURE_MERGE_JIT_AND_ENGINE) // Reference to the global holding the path to the JIT extern LPCWSTR g_CLRJITPath; #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE) #ifdef FEATURE_GDBJIT GetInfoForMethodDelegate getInfoForMethodDelegate = NULL; extern "C" int coreclr_create_delegate(void*, unsigned int, const char*, const char*, const char*, void**); #endif //FEATURE_GDBJIT // // Preload native assembly. // // This method allows to preload assembly to memory and apply relocations before initialization of CoreCLR. // Assemblies are stored in the list, which is scanned during general loading after CoreCLR initialization. // If path is found in the list, preloaded memory is used. // If CoreCLR is already initialized, it returns E_FAIL. // // Parameters: // assemblyPath - Absolute path of the assembly to preload // // Returns: // HRESULT indicating status of the operation. S_OK if the assembly was successfully preloaded // extern "C" DLLEXPORT int coreclr_preload_assembly( const char *assemblyPath ) { if (assemblyPath == NULL) { return E_FAIL; } return CorHost2::PreloadAssembly(assemblyPath); } // // Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain // // Parameters: // exePath - Absolute path of the executable that invoked the ExecuteAssembly (the native host application) // 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 // 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" DLLEXPORT int coreclr_initialize( const char* exePath, const char* appDomainFriendlyName, int propertyCount, const char** propertyKeys, const char** propertyValues, void** hostHandle, unsigned int* domainId) { HRESULT hr; #ifdef FEATURE_PAL DWORD error = PAL_InitializeCoreCLR(exePath); hr = HRESULT_FROM_WIN32(error); // If PAL initialization failed, then we should return right away and avoid // calling any other APIs because they can end up calling into the PAL layer again. if (FAILED(hr)) { return hr; } #endif ReleaseHolder host; hr = CorHost2::CreateObject(IID_ICLRRuntimeHost4, (void**)&host); IfFailRet(hr); ConstWStringHolder appDomainFriendlyNameW = StringToUnicode(appDomainFriendlyName); LPCWSTR* propertyKeysW; LPCWSTR* propertyValuesW; ConvertConfigPropertiesToUnicode( propertyKeys, propertyValues, propertyCount, &propertyKeysW, &propertyValuesW); // This will take ownership of propertyKeysWTemp and propertyValuesWTemp Configuration::InitializeConfigurationKnobs(propertyCount, propertyKeysW, propertyValuesW); #if !defined(FEATURE_MERGE_JIT_AND_ENGINE) // Fetch the path to JIT binary, if specified g_CLRJITPath = Configuration::GetKnobStringValue(W("JIT_PATH")); #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE) STARTUP_FLAGS startupFlags; InitializeStartupFlags(&startupFlags); hr = host->SetStartupFlags(startupFlags); IfFailRet(hr); hr = host->Start(); IfFailRet(hr); hr = host->CreateAppDomainWithManager( appDomainFriendlyNameW, // Flags: // APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS // - By default CoreCLR only allows platform neutral assembly to be run. To allow // assemblies marked as platform specific, include this flag // // APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP // - Allows sandboxed applications to make P/Invoke calls and use COM interop // // APPDOMAIN_SECURITY_SANDBOXED // - Enables sandboxing. If not set, the app is considered full trust // // APPDOMAIN_IGNORE_UNHANDLED_EXCEPTION // - Prevents the application from being torn down if a managed exception is unhandled // APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS | APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP | APPDOMAIN_DISABLE_TRANSPARENCY_ENFORCEMENT, NULL, // Name of the assembly that contains the AppDomainManager implementation NULL, // The AppDomainManager implementation type name propertyCount, propertyKeysW, propertyValuesW, (DWORD *)domainId); if (SUCCEEDED(hr)) { host.SuppressRelease(); *hostHandle = host; #ifdef FEATURE_GDBJIT HRESULT createDelegateResult; createDelegateResult = coreclr_create_delegate(*hostHandle, *domainId, "SOS.NETCore", "SOS.SymbolReader", "GetInfoForMethod", (void**)&getInfoForMethodDelegate); #if defined(_DEBUG) if (!SUCCEEDED(createDelegateResult)) { fprintf(stderr, "Can't create delegate for 'SOS.SymbolReader.GetInfoForMethod' " "method - status: 0x%08x\n", createDelegateResult); } #endif // _DEBUG #endif } 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" DLLEXPORT int coreclr_shutdown( void* hostHandle, unsigned int domainId) { ReleaseHolder host(reinterpret_cast(hostHandle)); HRESULT hr; hr = CorHost2::UnloadPreloadedAssemblies(); IfFailRet(hr); hr = host->UnloadAppDomain(domainId, true); // Wait until done IfFailRet(hr); hr = host->Stop(); #ifdef FEATURE_PAL PAL_Shutdown(); #endif 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 // latchedExitCode - Latched exit code after domain unloaded // // Returns: // HRESULT indicating status of the operation. S_OK if the assembly was successfully executed // extern "C" DLLEXPORT int coreclr_shutdown_2( void* hostHandle, unsigned int domainId, int* latchedExitCode) { ReleaseHolder host(reinterpret_cast(hostHandle)); HRESULT hr; hr = CorHost2::UnloadPreloadedAssemblies(); IfFailRet(hr); hr = host->UnloadAppDomain2(domainId, true, latchedExitCode); // Wait until done IfFailRet(hr); hr = host->Stop(); #ifdef FEATURE_PAL PAL_Shutdown(); #endif return hr; } // // Create a native callable function pointer 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 native callable function 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" DLLEXPORT int coreclr_create_delegate( void* hostHandle, unsigned int domainId, const char* entryPointAssemblyName, const char* entryPointTypeName, const char* entryPointMethodName, void** delegate) { ICLRRuntimeHost4* 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" DLLEXPORT 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; ICLRRuntimeHost4* host = reinterpret_cast(hostHandle); ConstWStringArrayHolder argvW; argvW.Set(StringArrayToUnicode(argc, argv), argc); ConstWStringHolder managedAssemblyPathW = StringToUnicode(managedAssemblyPath); HRESULT hr = host->ExecuteAssembly(domainId, managedAssemblyPathW, argc, argvW, (DWORD *)exitCode); IfFailRet(hr); return hr; }