HRESULT CoreClrEmbedding::Initialize(BOOL debugMode) { // Much of the CoreCLR bootstrapping process is cribbed from // https://github.com/aspnet/dnx/blob/dev/src/dnx.coreclr.unix/dnx.coreclr.cpp DBG("CoreClrEmbedding::Initialize - Started") HRESULT result = S_OK; char currentDirectory[PATH_MAX]; #ifdef EDGE_PLATFORM_WINDOWS if (!_getcwd(¤tDirectory[0], PATH_MAX)) #else if (!getcwd(¤tDirectory[0], PATH_MAX)) #endif { throwV8Exception("Unable to get the current directory."); return E_FAIL; } char edgeNodePath[PATH_MAX]; #ifdef EDGE_PLATFORM_WINDOWS HMODULE moduleHandle = NULL; GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) &CoreClrEmbedding::Initialize, &moduleHandle); GetModuleFileName(moduleHandle, edgeNodePath, PATH_MAX); PathRemoveFileSpec(edgeNodePath); #else Dl_info dlInfo; dladdr((void*)&CoreClrEmbedding::Initialize, &dlInfo); strcpy(edgeNodePath, dlInfo.dli_fname); strcpy(edgeNodePath, dirname(edgeNodePath)); #endif DBG("CoreClrEmbedding::Initialize - edge.node path is %s", edgeNodePath); void* libCoreClr = NULL; char bootstrapper[PATH_MAX]; GetPathToBootstrapper(&bootstrapper[0], PATH_MAX); DBG("CoreClrEmbedding::Initialize - Bootstrapper is %s", bootstrapper); char coreClrDirectory[PATH_MAX]; char* coreClrEnvironmentVariable = getenv("CORECLR_DIR"); if (coreClrEnvironmentVariable) { if (coreClrEnvironmentVariable[0] == '"') { strncpy(&coreClrDirectory[0], &coreClrEnvironmentVariable[1], strlen(coreClrEnvironmentVariable) - 2); coreClrDirectory[strlen(coreClrEnvironmentVariable) - 2] = '\0'; } else { strncpy(&coreClrDirectory[0], coreClrEnvironmentVariable, strlen(coreClrEnvironmentVariable) + 1); } DBG("CoreClrEmbedding::Initialize - Trying to load %s from the path specified in the CORECLR_DIR environment variable: %s", LIBCORECLR_NAME, coreClrDirectory); LoadCoreClrAtPath(coreClrDirectory, &libCoreClr); } if (!libCoreClr) { strncpy(&coreClrDirectory[0], currentDirectory, strlen(currentDirectory) + 1); LoadCoreClrAtPath(coreClrDirectory, &libCoreClr); } if (!libCoreClr) { // Try to load CoreCLR from application path #ifdef EDGE_PLATFORM_WINDOWS char* lastSlash = strrchr(&bootstrapper[0], '\\'); #else char* lastSlash = strrchr(&bootstrapper[0], '/'); #endif assert(lastSlash); strncpy(&coreClrDirectory[0], &bootstrapper[0], lastSlash - &bootstrapper[0]); coreClrDirectory[lastSlash - &bootstrapper[0]] = '\0'; LoadCoreClrAtPath(coreClrDirectory, &libCoreClr); } if (!libCoreClr) { std::string pathEnvironmentVariable = getenv("PATH"); #if EDGE_PLATFORM_WINDOWS char delimeter = ';'; #else char delimeter = ':'; #endif size_t previousIndex = 0; size_t currentIndex = pathEnvironmentVariable.find(delimeter); while (!libCoreClr && currentIndex != std::string::npos) { strncpy(&coreClrDirectory[0], pathEnvironmentVariable.substr(previousIndex, currentIndex - previousIndex).c_str(), currentIndex - previousIndex); coreClrDirectory[currentIndex - previousIndex] = '\0'; LoadCoreClrAtPath(coreClrDirectory, &libCoreClr); if (!libCoreClr) { previousIndex = currentIndex + 1; currentIndex = pathEnvironmentVariable.find(delimeter, previousIndex); } } } if (!libCoreClr) { throwV8Exception("Failed to find CoreCLR. Make sure that you have either specified the CoreCLR directory in the CORECLR_DIR environment variable or it exists somewhere in your PATH environment variable, which you do via the \"dnvm install\" and \"dnvm use\" commands."); return E_FAIL; } DBG("CoreClrEmbedding::Initialize - %s loaded successfully from %s", LIBCORECLR_NAME, &coreClrDirectory[0]); std::string assemblySearchDirectories; assemblySearchDirectories.append(¤tDirectory[0]); assemblySearchDirectories.append(":"); assemblySearchDirectories.append(&coreClrDirectory[0]); DBG("CoreClrEmbedding::Initialize - Assembly search path is %s", assemblySearchDirectories.c_str()); coreclr_initializeFunction initializeCoreCLR = (coreclr_initializeFunction) LoadSymbol(libCoreClr, "coreclr_initialize"); if (!initializeCoreCLR) { throwV8Exception("Error loading the coreclr_initialize function from %s: %s.", LIBCORECLR_NAME, GetLoadError()); return E_FAIL; } DBG("CoreClrEmbedding::Initialize - coreclr_initialize loaded successfully"); coreclr_create_delegateFunction createDelegate = (coreclr_create_delegateFunction) LoadSymbol(libCoreClr, "coreclr_create_delegate"); if (!createDelegate) { throwV8Exception("Error loading the coreclr_create_delegate function from %s: %s.", LIBCORECLR_NAME, GetLoadError()); return E_FAIL; } DBG("CoreClrEmbedding::Initialize - coreclr_create_delegate loaded successfully"); const char* propertyKeys[] = { "TRUSTED_PLATFORM_ASSEMBLIES", "APP_PATHS", "APP_NI_PATHS", "NATIVE_DLL_SEARCH_DIRECTORIES", "AppDomainCompatSwitch" }; std::string tpaList; AddToTpaList(coreClrDirectory, &tpaList); std::string appPaths(¤tDirectory[0]); #if EDGE_PLATFORM_WINDOWS appPaths.append(";"); #else appPaths.append(":"); #endif appPaths.append(edgeNodePath); DBG("CoreClrEmbedding::Initialize - Using %s as the app path value", appPaths.c_str()); const char* propertyValues[] = { tpaList.c_str(), appPaths.c_str(), appPaths.c_str(), assemblySearchDirectories.c_str(), "UseLatestBehaviorWhenTFMNotSpecified" }; DBG("CoreClrEmbedding::Initialize - Calling coreclr_initialize()"); result = initializeCoreCLR( bootstrapper, "Edge", sizeof(propertyKeys) / sizeof(propertyKeys[0]), &propertyKeys[0], &propertyValues[0], &hostHandle, &appDomainId); if (FAILED(result)) { throwV8Exception("Call to coreclr_initialize() failed with a return code of 0x%x.", result); return result; } DBG("CoreClrEmbedding::Initialize - CoreCLR initialized successfully"); DBG("CoreClrEmbedding::Initialize - App domain created successfully (app domain ID: %d)", appDomainId); SetCallV8FunctionDelegateFunction setCallV8Function; CREATE_DELEGATE("GetFunc", &getFunc); CREATE_DELEGATE("CallFunc", &callFunc); CREATE_DELEGATE("ContinueTask", &continueTask); CREATE_DELEGATE("FreeHandle", &freeHandle); CREATE_DELEGATE("FreeMarshalData", &freeMarshalData); CREATE_DELEGATE("SetCallV8FunctionDelegate", &setCallV8Function); CREATE_DELEGATE("CompileFunc", &compileFunc); CREATE_DELEGATE("Initialize", &initialize); DBG("CoreClrEmbedding::Initialize - Getting runtime info"); CoreClrGcHandle exception = NULL; BootstrapperContext context; context.runtimeDirectory = &coreClrDirectory[0]; context.applicationDirectory = getenv("EDGE_APP_ROOT"); context.edgeNodePath = &edgeNodePath[0]; if (!context.applicationDirectory) { context.applicationDirectory = ¤tDirectory[0]; } std::string operatingSystem = GetOSName(); std::string operatingSystemVersion = GetOSVersion(); context.architecture = GetOSArchitecture(); context.operatingSystem = operatingSystem.c_str(); context.operatingSystemVersion = operatingSystemVersion.c_str(); DBG("CoreClrEmbedding::Initialize - Operating system: %s", context.operatingSystem); DBG("CoreClrEmbedding::Initialize - Operating system version: %s", context.operatingSystemVersion); DBG("CoreClrEmbedding::Initialize - Architecture: %s", context.architecture); DBG("CoreClrEmbedding::Initialize - Runtime directory: %s", context.runtimeDirectory); DBG("CoreClrEmbedding::Initialize - Application directory: %s", context.applicationDirectory); DBG("CoreClrEmbedding::Initialize - Calling CLR Initialize() function"); initialize(&context, &exception); if (exception) { v8::Local<v8::Value> v8Exception = CoreClrFunc::MarshalCLRToV8(exception, V8TypeException); FreeMarshalData(exception, V8TypeException); throwV8Exception(v8Exception); } else { DBG("CoreClrEmbedding::Initialize - CLR Initialize() function called successfully") } exception = NULL; setCallV8Function(CoreClrNodejsFunc::Call, &exception); if (exception) { v8::Local<v8::Value> v8Exception = CoreClrFunc::MarshalCLRToV8(exception, V8TypeException); FreeMarshalData(exception, V8TypeException); throwV8Exception(v8Exception); } else { DBG("CoreClrEmbedding::Initialize - CallV8Function delegate set successfully"); } DBG("CoreClrEmbedding::Initialize - Completed"); return S_OK; }
extern "C" HRESULT CallApplicationMain(PCALL_APPLICATION_MAIN_DATA data) { HRESULT hr = S_OK; size_t cchTrustedPlatformAssemblies = 0; std::string runtimeDirectory; if (data->runtimeDirectory) { runtimeDirectory = data->runtimeDirectory; } else { // TODO: This should get the directory that this library is in, not the CWD. char szCurrentDirectory[PATH_MAX]; if (!getcwd(szCurrentDirectory, PATH_MAX)) { return E_FAIL; } runtimeDirectory = std::string(szCurrentDirectory); } std::string coreClrDirectory; void* coreClr = LoadCoreClr(coreClrDirectory); if (!coreClr) { char* error = dlerror(); fprintf(stderr, "failed to locate coreclr.dll with error %s\n", error); return E_FAIL; } const char* property_keys[] = { "APPBASE", "TRUSTED_PLATFORM_ASSEMBLIES", "APP_PATHS", }; std::string trustedPlatformAssemblies; // Try native images first if (!GetTrustedPlatformAssembliesList(coreClrDirectory.c_str(), true, trustedPlatformAssemblies)) { if (!GetTrustedPlatformAssembliesList(coreClrDirectory.c_str(), false, trustedPlatformAssemblies)) { fprintf(stderr, "Failed to find files in the coreclr directory\n"); hr = E_FAIL; return hr; } } // Add the assembly containing the app domain manager to the trusted list trustedPlatformAssemblies.append(runtimeDirectory); trustedPlatformAssemblies.append("dnx.coreclr.managed.dll"); std::string appPaths(runtimeDirectory); appPaths.append(":"); appPaths.append(coreClrDirectory); appPaths.append(":"); const char* property_values[] = { // APPBASE data->applicationBase, // TRUESTED_PLATFORM_ASSEMBLIES trustedPlatformAssemblies.c_str(), // APP_PATHS appPaths.c_str(), }; ExecuteAssemblyFunction executeAssembly = (ExecuteAssemblyFunction)dlsym(coreClr, "ExecuteAssembly"); if (!executeAssembly) { fprintf(stderr, "Could not find ExecuteAssembly entrypoint in coreclr.\n"); return E_FAIL; } setenv("DNX_FRAMEWORK", "dnxcore50", 1); std::string coreClrDllPath(coreClrDirectory); coreClrDllPath.append("/"); coreClrDllPath.append("libcoreclr.so"); char pathToBootstrapper[PATH_MAX]; ssize_t pathLen = readlink("/proc/self/exe", pathToBootstrapper, PATH_MAX - 1); if (pathLen == -1) { fprintf(stderr, "Could not locate full bootstrapper path.\n"); return E_FAIL; } pathToBootstrapper[pathLen] = '\0'; hr = executeAssembly(pathToBootstrapper, coreClrDllPath.c_str(), "dnx.coreclr.managed", sizeof(property_keys) / sizeof(property_keys[0]), property_keys, property_values, data->argc, (const char**)data->argv, nullptr, "dnx.coreclr.managed, Version=0.1.0.0", "DomainManager", "Execute", (DWORD*)&(data->exitcode)); dlclose(coreClr); return hr; }
extern "C" __declspec(dllexport) bool __stdcall CallApplicationMain(PCALL_APPLICATION_MAIN_DATA data) { HRESULT hr = S_OK; FnGetCLRRuntimeHost pfnGetCLRRuntimeHost = nullptr; ICLRRuntimeHost2* pCLRRuntimeHost = nullptr; TCHAR szCurrentDirectory[MAX_PATH]; TCHAR szCoreClrDirectory[MAX_PATH]; TCHAR lpCoreClrModulePath[MAX_PATH]; GetModuleDirectory(NULL, szCurrentDirectory); HMODULE hCoreCLRModule = LoadCoreClr(); if (!hCoreCLRModule) { printf_s("Failed to locate coreclr.dll.\n"); return false; } // Get the path to the module DWORD dwCoreClrModulePathSize = GetModuleFileName(hCoreCLRModule, lpCoreClrModulePath, MAX_PATH); lpCoreClrModulePath[dwCoreClrModulePathSize] = '\0'; GetModuleDirectory(hCoreCLRModule, szCoreClrDirectory); HMODULE ignoreModule; // Pin the module - CoreCLR.dll does not support being unloaded. if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, lpCoreClrModulePath, &ignoreModule)) { printf_s("Failed to pin coreclr.dll.\n"); return false; } pfnGetCLRRuntimeHost = (FnGetCLRRuntimeHost)::GetProcAddress(hCoreCLRModule, "GetCLRRuntimeHost"); if (!pfnGetCLRRuntimeHost) { printf_s("Failed to find export GetCLRRuntimeHost.\n"); return false; } hr = pfnGetCLRRuntimeHost(IID_ICLRRuntimeHost2, (IUnknown**)&pCLRRuntimeHost); if (FAILED(hr)) { printf_s("Failed to get IID_ICLRRuntimeHost2.\n"); return false; } STARTUP_FLAGS dwStartupFlags = (STARTUP_FLAGS)( STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_FLAGS::STARTUP_SINGLE_APPDOMAIN ); pCLRRuntimeHost->SetStartupFlags(dwStartupFlags); // Authenticate with either CORECLR_HOST_AUTHENTICATION_KEY or CORECLR_HOST_AUTHENTICATION_KEY_NONGEN hr = pCLRRuntimeHost->Authenticate(CORECLR_HOST_AUTHENTICATION_KEY); if (FAILED(hr)) { printf_s("Failed to Authenticate().\n"); return false; } hr = pCLRRuntimeHost->Start(); if (FAILED(hr)) { printf_s("Failed to Start().\n"); return false; } const wchar_t* property_keys[] = { // Allowed property names: // APPBASE // - The base path of the application from which the exe and other assemblies will be loaded L"APPBASE", // // TRUSTED_PLATFORM_ASSEMBLIES // - The list of complete paths to each of the fully trusted assemblies L"TRUSTED_PLATFORM_ASSEMBLIES", // // APP_PATHS // - The list of paths which will be probed by the assembly loader L"APP_PATHS", // // APP_NI_PATHS // - The list of additional paths that the assembly loader will probe for ngen images // // NATIVE_DLL_SEARCH_DIRECTORIES // - The list of paths that will be probed for native DLLs called by PInvoke // }; wstring trustedPlatformAssemblies(L""); // Enumerate the core clr directory and add each .dll or .ni.dll to the list of trusted assemblies wstring pattern(szCoreClrDirectory); pattern += L"*.dll"; WIN32_FIND_DATA ffd; HANDLE findHandle = FindFirstFile(pattern.c_str(), &ffd); if (INVALID_HANDLE_VALUE == findHandle) { printf_s("Failed to find files in the coreclr directory\n"); return -1; } do { if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // Skip directories } else { trustedPlatformAssemblies += szCoreClrDirectory; trustedPlatformAssemblies += ffd.cFileName; trustedPlatformAssemblies += L";"; } } while (FindNextFile(findHandle, &ffd) != 0); // Add the assembly containing the app domain manager to the trusted list trustedPlatformAssemblies += szCurrentDirectory; trustedPlatformAssemblies += L"klr.core45.managed.dll"; wstring appPaths(szCurrentDirectory); appPaths += L";"; appPaths += szCoreClrDirectory; const wchar_t* property_values[] = { // APPBASE szCurrentDirectory, // TODO: Allow overriding this // TRUSTED_PLATFORM_ASSEMBLIES trustedPlatformAssemblies.c_str(), // APP_PATHS appPaths.c_str(), }; DWORD domainId; DWORD dwFlagsAppDomain = APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS | APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP | APPDOMAIN_ENABLE_ASSEMBLY_LOADFILE; LPCWSTR szAssemblyName = L"klr.core45.managed, Version=1.0.0.0"; LPCWSTR szDomainManagerTypeName = L"DomainManager"; LPCWSTR szMainMethodName = L"Main"; int nprops = sizeof(property_keys) / sizeof(wchar_t*); hr = pCLRRuntimeHost->CreateAppDomainWithManager( L"klr.core45.managed", dwFlagsAppDomain, szAssemblyName, szDomainManagerTypeName, nprops, property_keys, property_values, &domainId); if (FAILED(hr)) { printf_s("Failed to create app domain (%d).\n", hr); return false; } HostMain pHostMain; hr = pCLRRuntimeHost->CreateDelegate( domainId, szAssemblyName, szDomainManagerTypeName, szMainMethodName, (INT_PTR*)&pHostMain); if (FAILED(hr)) { printf_s("Failed to create main delegate (%d).\n", hr); return false; } // Call main data->exitcode = pHostMain(szCurrentDirectory, data->argc, data->argv); pCLRRuntimeHost->UnloadAppDomain(domainId, true); pCLRRuntimeHost->Stop(); return hr; }