From ba7bab476501c16e437b0aee71c1be02c3dda176 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 3 May 2021 15:55:48 -0700 Subject: Move Bal.wixext into ext --- src/ext/Bal/dnchost/coreclrhost.h | 137 ++++++++++++ src/ext/Bal/dnchost/dnchost.cpp | 309 +++++++++++++++++++++++++++ src/ext/Bal/dnchost/dnchost.def | 6 + src/ext/Bal/dnchost/dnchost.h | 35 +++ src/ext/Bal/dnchost/dnchost.vcxproj | 106 ++++++++++ src/ext/Bal/dnchost/dncutil.cpp | 411 ++++++++++++++++++++++++++++++++++++ src/ext/Bal/dnchost/dncutil.h | 38 ++++ src/ext/Bal/dnchost/packages.config | 13 ++ src/ext/Bal/dnchost/precomp.cpp | 3 + src/ext/Bal/dnchost/precomp.h | 31 +++ 10 files changed, 1089 insertions(+) create mode 100644 src/ext/Bal/dnchost/coreclrhost.h create mode 100644 src/ext/Bal/dnchost/dnchost.cpp create mode 100644 src/ext/Bal/dnchost/dnchost.def create mode 100644 src/ext/Bal/dnchost/dnchost.h create mode 100644 src/ext/Bal/dnchost/dnchost.vcxproj create mode 100644 src/ext/Bal/dnchost/dncutil.cpp create mode 100644 src/ext/Bal/dnchost/dncutil.h create mode 100644 src/ext/Bal/dnchost/packages.config create mode 100644 src/ext/Bal/dnchost/precomp.cpp create mode 100644 src/ext/Bal/dnchost/precomp.h (limited to 'src/ext/Bal/dnchost') diff --git a/src/ext/Bal/dnchost/coreclrhost.h b/src/ext/Bal/dnchost/coreclrhost.h new file mode 100644 index 00000000..07f28735 --- /dev/null +++ b/src/ext/Bal/dnchost/coreclrhost.h @@ -0,0 +1,137 @@ +// 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. + + + +// ***** ABOUT THIS HEADER ***** +// ************************************************************************************** +// +// This is the version on 2019-12-22 from +// https://github.com/dotnet/runtime/blob/master/src/coreclr/src/coreclr/hosts/inc/coreclrhost.h +// +// ************************************************************************************** +// **************************** + + +// +// APIs for hosting CoreCLR +// + +#ifndef __CORECLR_HOST_H__ +#define __CORECLR_HOST_H__ + +#if defined(_WIN32) && defined(_M_IX86) +#define CORECLR_CALLING_CONVENTION __stdcall +#else +#define CORECLR_CALLING_CONVENTION +#endif + +// For each hosting API, we define a function prototype and a function pointer +// The prototype is useful for implicit linking against the dynamic coreclr +// library and the pointer for explicit dynamic loading (dlopen, LoadLibrary) +#define CORECLR_HOSTING_API(function, ...) \ + extern "C" int CORECLR_CALLING_CONVENTION function(__VA_ARGS__); \ + typedef int (CORECLR_CALLING_CONVENTION *function##_ptr)(__VA_ARGS__) + +// +// 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 +// +CORECLR_HOSTING_API(coreclr_initialize, + const char* exePath, + const char* appDomainFriendlyName, + int propertyCount, + const char** propertyKeys, + const char** propertyValues, + void** hostHandle, + unsigned int* domainId); + +// +// 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 +// +CORECLR_HOSTING_API(coreclr_shutdown, + void* hostHandle, + unsigned int domainId); + +// +// 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 +// +CORECLR_HOSTING_API(coreclr_shutdown_2, + void* hostHandle, + unsigned int domainId, + int* latchedExitCode); + +// +// 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 +// +CORECLR_HOSTING_API(coreclr_create_delegate, + void* hostHandle, + unsigned int domainId, + const char* entryPointAssemblyName, + const char* entryPointTypeName, + const char* entryPointMethodName, + void** delegate); + +// +// 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 +// +CORECLR_HOSTING_API(coreclr_execute_assembly, + void* hostHandle, + unsigned int domainId, + int argc, + const char** argv, + const char* managedAssemblyPath, + unsigned int* exitCode); + +#undef CORECLR_HOSTING_API + +#endif // __CORECLR_HOST_H__ \ No newline at end of file diff --git a/src/ext/Bal/dnchost/dnchost.cpp b/src/ext/Bal/dnchost/dnchost.cpp new file mode 100644 index 00000000..8ca326fc --- /dev/null +++ b/src/ext/Bal/dnchost/dnchost.cpp @@ -0,0 +1,309 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static DNCSTATE vstate = { }; + + +// internal function declarations + +static HRESULT LoadModulePaths( + __in DNCSTATE* pState + ); +static HRESULT LoadDncConfiguration( + __in DNCSTATE* pState, + __in const BOOTSTRAPPER_CREATE_ARGS* pArgs + ); +static HRESULT LoadRuntime( + __in DNCSTATE* pState + ); +static HRESULT LoadManagedBootstrapperApplicationFactory( + __in DNCSTATE* pState + ); +static HRESULT CreatePrerequisiteBA( + __in HRESULT hrHostInitialization, + __in IBootstrapperEngine* pEngine, + __in LPCWSTR wzAppBase, + __in const BOOTSTRAPPER_CREATE_ARGS* pArgs, + __inout BOOTSTRAPPER_CREATE_RESULTS* pResults + ); + + +// function definitions + +extern "C" BOOL WINAPI DllMain( + IN HINSTANCE hInstance, + IN DWORD dwReason, + IN LPVOID /* pvReserved */ + ) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + ::DisableThreadLibraryCalls(hInstance); + vstate.hInstance = hInstance; + break; + + case DLL_PROCESS_DETACH: + vstate.hInstance = NULL; + break; + } + + return TRUE; +} + +extern "C" HRESULT WINAPI BootstrapperApplicationCreate( + __in const BOOTSTRAPPER_CREATE_ARGS* pArgs, + __inout BOOTSTRAPPER_CREATE_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + HRESULT hrHostInitialization = S_OK; + IBootstrapperEngine* pEngine = NULL; + + // coreclr.dll doesn't support unloading, so the rest of the .NET Core hosting stack doesn't support it either. + // This means we also can't unload. + pResults->fDisableUnloading = TRUE; + + hr = BalInitializeFromCreateArgs(pArgs, &pEngine); + ExitOnFailure(hr, "Failed to initialize Bal."); + + if (!vstate.fInitialized) + { + hr = XmlInitialize(); + BalExitOnFailure(hr, "Failed to initialize XML."); + + hr = LoadModulePaths(&vstate); + BalExitOnFailure(hr, "Failed to get the host base path."); + + hr = LoadDncConfiguration(&vstate, pArgs); + BalExitOnFailure(hr, "Failed to get the dnc configuration."); + + vstate.fInitialized = TRUE; + } + + if (!vstate.fInitializedRuntime) + { + hr = LoadRuntime(&vstate); + + vstate.fInitializedRuntime = SUCCEEDED(hr); + } + + if (vstate.fInitializedRuntime) + { + if (!vstate.pAppFactory) + { + hr = LoadManagedBootstrapperApplicationFactory(&vstate); + BalExitOnFailure(hr, "Failed to create the .NET Core bootstrapper application factory."); + } + + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading .NET Core %ls bootstrapper application.", DNCHOSTTYPE_FDD == vstate.type ? L"FDD" : L"SCD"); + + hr = vstate.pAppFactory->Create(pArgs, pResults); + BalExitOnFailure(hr, "Failed to create the .NET Core bootstrapper application."); + } + else // fallback to the prerequisite BA. + { + if (DNCHOSTTYPE_SCD == vstate.type) + { + hrHostInitialization = E_DNCHOST_SCD_RUNTIME_FAILURE; + BalLogError(hr, "The self-contained .NET Core runtime failed to load. This is an unrecoverable error."); + } + else + { + hrHostInitialization = S_OK; + } + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading prerequisite bootstrapper application because .NET Core host could not be loaded, error: 0x%08x.", hr); + + hr = CreatePrerequisiteBA(hrHostInitialization, pEngine, vstate.sczAppBase, pArgs, pResults); + BalExitOnFailure(hr, "Failed to create the pre-requisite bootstrapper application."); + } + +LExit: + ReleaseNullObject(pEngine); + + return hr; +} + +extern "C" void WINAPI BootstrapperApplicationDestroy() +{ + if (vstate.hMbapreqModule) + { + PFN_BOOTSTRAPPER_APPLICATION_DESTROY pfnDestroy = reinterpret_cast(::GetProcAddress(vstate.hMbapreqModule, "DncPrereqBootstrapperApplicationDestroy")); + if (pfnDestroy) + { + (*pfnDestroy)(); + } + + ::FreeLibrary(vstate.hMbapreqModule); + vstate.hMbapreqModule = NULL; + } + + BalUninitialize(); +} + +static HRESULT LoadModulePaths( + __in DNCSTATE* pState + ) +{ + HRESULT hr = S_OK; + + hr = PathForCurrentProcess(&pState->sczModuleFullPath, pState->hInstance); + BalExitOnFailure(hr, "Failed to get the full host path."); + + hr = PathGetDirectory(pState->sczModuleFullPath, &pState->sczAppBase); + BalExitOnFailure(hr, "Failed to get the directory of the full process path."); + + hr = PathConcat(pState->sczAppBase, DNC_ASSEMBLY_FILE_NAME, &pState->sczManagedHostPath); + BalExitOnFailure(hr, "Failed to create managed host path."); + +LExit: + return hr; +} + +static HRESULT LoadDncConfiguration( + __in DNCSTATE* pState, + __in const BOOTSTRAPPER_CREATE_ARGS* pArgs + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixdManifest = NULL; + IXMLDOMNode* pixnHost = NULL; + LPWSTR sczPayloadName = NULL; + DWORD dwBool = 0; + + hr = XmlLoadDocumentFromFile(pArgs->pCommand->wzBootstrapperApplicationDataPath, &pixdManifest); + BalExitOnFailure(hr, "Failed to load BalManifest '%ls'", pArgs->pCommand->wzBootstrapperApplicationDataPath); + + hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixBalBAFactoryAssembly", &pixnHost); + BalExitOnFailure(hr, "Failed to get WixBalBAFactoryAssembly element."); + + if (S_FALSE == hr) + { + hr = E_NOTFOUND; + BalExitOnRootFailure(hr, "Failed to find WixBalBAFactoryAssembly element in bootstrapper application config."); + } + + hr = XmlGetAttributeEx(pixnHost, L"FilePath", &sczPayloadName); + BalExitOnFailure(hr, "Failed to get WixBalBAFactoryAssembly/@FilePath."); + + hr = PathConcat(pArgs->pCommand->wzBootstrapperWorkingFolder, sczPayloadName, &pState->sczBaFactoryAssemblyPath); + BalExitOnFailure(hr, "Failed to create BaFactoryAssemblyPath."); + + LPCWSTR wzFileName = PathFile(pState->sczBaFactoryAssemblyPath); + LPCWSTR wzExtension = PathExtension(pState->sczBaFactoryAssemblyPath); + if (!wzExtension) + { + BalExitOnFailure(hr = E_FAIL, "BaFactoryAssemblyPath has no extension."); + } + + hr = StrAllocString(&pState->sczBaFactoryAssemblyName, wzFileName, wzExtension - wzFileName); + BalExitOnFailure(hr, "Failed to copy BAFactoryAssembly payload Name."); + + hr = StrAllocString(&pState->sczBaFactoryDepsJsonPath, pState->sczBaFactoryAssemblyPath, wzExtension - pState->sczBaFactoryAssemblyPath); + BalExitOnFailure(hr, "Failed to initialize deps json path."); + + hr = StrAllocString(&pState->sczBaFactoryRuntimeConfigPath, pState->sczBaFactoryDepsJsonPath, 0); + BalExitOnFailure(hr, "Failed to initialize runtime config path."); + + hr = StrAllocConcat(&pState->sczBaFactoryDepsJsonPath, L".deps.json", 0); + BalExitOnFailure(hr, "Failed to concat extension to deps json path."); + + hr = StrAllocConcat(&pState->sczBaFactoryRuntimeConfigPath, L".runtimeconfig.json", 0); + BalExitOnFailure(hr, "Failed to concat extension to runtime config path."); + + pState->type = DNCHOSTTYPE_FDD; + + hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixDncOptions", &pixnHost); + if (S_FALSE == hr) + { + ExitFunction1(hr = S_OK); + } + BalExitOnFailure(hr, "Failed to find WixDncOptions element in bootstrapper application config."); + + hr = XmlGetAttributeNumber(pixnHost, L"SelfContainedDeployment", &dwBool); + if (S_FALSE == hr) + { + hr = S_OK; + } + else if (SUCCEEDED(hr) && dwBool) + { + pState->type = DNCHOSTTYPE_SCD; + } + BalExitOnFailure(hr, "Failed to get SelfContainedDeployment value."); + +LExit: + ReleaseStr(sczPayloadName); + ReleaseObject(pixnHost); + ReleaseObject(pixdManifest); + + return hr; +} + +static HRESULT LoadRuntime( + __in DNCSTATE* pState + ) +{ + HRESULT hr = S_OK; + + hr = DnchostLoadRuntime( + &pState->hostfxrState, + pState->sczModuleFullPath, + pState->sczManagedHostPath, + pState->sczBaFactoryDepsJsonPath, + pState->sczBaFactoryRuntimeConfigPath); + + return hr; +} + +static HRESULT LoadManagedBootstrapperApplicationFactory( + __in DNCSTATE* pState + ) +{ + HRESULT hr = S_OK; + + hr = DnchostCreateFactory( + &pState->hostfxrState, + pState->sczBaFactoryAssemblyName, + pState->sczBaFactoryAssemblyPath, + &pState->pAppFactory); + + return hr; +} + +static HRESULT CreatePrerequisiteBA( + __in HRESULT hrHostInitialization, + __in IBootstrapperEngine* pEngine, + __in LPCWSTR wzAppBase, + __in const BOOTSTRAPPER_CREATE_ARGS* pArgs, + __inout BOOTSTRAPPER_CREATE_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDncpreqPath = NULL; + HMODULE hModule = NULL; + + hr = PathConcat(wzAppBase, L"dncpreq.dll", &sczDncpreqPath); + BalExitOnFailure(hr, "Failed to get path to pre-requisite BA."); + + hModule = ::LoadLibraryW(sczDncpreqPath); + BalExitOnNullWithLastError(hModule, hr, "Failed to load pre-requisite BA DLL."); + + PFN_DNCPREQ_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = reinterpret_cast(::GetProcAddress(hModule, "DncPrereqBootstrapperApplicationCreate")); + BalExitOnNullWithLastError(pfnCreate, hr, "Failed to get DncPrereqBootstrapperApplicationCreate entry-point from: %ls", sczDncpreqPath); + + hr = pfnCreate(hrHostInitialization, pEngine, pArgs, pResults); + BalExitOnFailure(hr, "Failed to create prequisite bootstrapper app."); + + vstate.hMbapreqModule = hModule; + hModule = NULL; + +LExit: + if (hModule) + { + ::FreeLibrary(hModule); + } + ReleaseStr(sczDncpreqPath); + + return hr; +} diff --git a/src/ext/Bal/dnchost/dnchost.def b/src/ext/Bal/dnchost/dnchost.def new file mode 100644 index 00000000..4488df94 --- /dev/null +++ b/src/ext/Bal/dnchost/dnchost.def @@ -0,0 +1,6 @@ +; Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +EXPORTS + BootstrapperApplicationCreate + BootstrapperApplicationDestroy diff --git a/src/ext/Bal/dnchost/dnchost.h b/src/ext/Bal/dnchost/dnchost.h new file mode 100644 index 00000000..22fd8f5e --- /dev/null +++ b/src/ext/Bal/dnchost/dnchost.h @@ -0,0 +1,35 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +enum DNCHOSTTYPE +{ + DNCHOSTTYPE_UNKNOWN, + DNCHOSTTYPE_FDD, + DNCHOSTTYPE_SCD, +}; + +extern "C" typedef HRESULT(WINAPI* PFN_DNCPREQ_BOOTSTRAPPER_APPLICATION_CREATE)( + __in HRESULT hrHostInitialization, + __in IBootstrapperEngine* pEngine, + __in const BOOTSTRAPPER_CREATE_ARGS* pArgs, + __inout BOOTSTRAPPER_CREATE_RESULTS* pResults + ); + +struct DNCSTATE +{ + BOOL fInitialized; + BOOL fInitializedRuntime; + HINSTANCE hInstance; + LPWSTR sczModuleFullPath; + LPWSTR sczAppBase; + LPWSTR sczManagedHostPath; + LPWSTR sczBaFactoryAssemblyName; + LPWSTR sczBaFactoryAssemblyPath; + LPWSTR sczBaFactoryDepsJsonPath; + LPWSTR sczBaFactoryRuntimeConfigPath; + DNCHOSTTYPE type; + HOSTFXR_STATE hostfxrState; + IBootstrapperApplicationFactory* pAppFactory; + HMODULE hMbapreqModule; +}; diff --git a/src/ext/Bal/dnchost/dnchost.vcxproj b/src/ext/Bal/dnchost/dnchost.vcxproj new file mode 100644 index 00000000..bef3f77e --- /dev/null +++ b/src/ext/Bal/dnchost/dnchost.vcxproj @@ -0,0 +1,106 @@ + + + + + + + + + + + + Debug + ARM64 + + + Release + ARM64 + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {B6F70281-6583-4138-BB7F-AABFEBBB3CA2} + DynamicLibrary + v142 + Unicode + dnchost + dnchost.def + + + + + $(Platform) + x86 + ..\..\packages\runtime.win-$(NetHostPlatform).Microsoft.NETCore.DotNetAppHost.5.0.0\runtimes\win-$(NetHostPlatform)\native\ + shlwapi.lib;$(NetHostPath)libnethost.lib + + + + + + Create + + + + + + + + + + + + + + + + + $(BaseOutputPath)obj;$(NetHostPath);%(AdditionalIncludeDirectories) + + + /LTCG %(AdditionalOptions) + + + + + {0D780900-C2FF-4FA2-8CB5-8A19768724C5} + false + true + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ext/Bal/dnchost/dncutil.cpp b/src/ext/Bal/dnchost/dncutil.cpp new file mode 100644 index 00000000..34d14911 --- /dev/null +++ b/src/ext/Bal/dnchost/dncutil.cpp @@ -0,0 +1,411 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// https://github.com/dotnet/runtime/blob/master/src/installer/corehost/error_codes.h +#define InvalidArgFailure 0x80008081 +#define HostApiBufferTooSmall 0x80008098 +#define HostApiUnsupportedVersion 0x800080a2 + +// internal function declarations + +static HRESULT GetHostfxrPath( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzNativeHostPath + ); +static HRESULT LoadHostfxr( + __in HOSTFXR_STATE* pState + ); +static HRESULT InitializeHostfxr( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzManagedHostPath, + __in LPCWSTR wzDepsJsonPath, + __in LPCWSTR wzRuntimeConfigPath + ); +static HRESULT InitializeCoreClr( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzNativeHostPath + ); +static HRESULT InitializeCoreClrPre5( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzNativeHostPath + ); +static HRESULT LoadCoreClr( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzCoreClrPath + ); +static HRESULT StartCoreClr( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzNativeHostPath, + __in DWORD cProperties, + __in LPCWSTR* propertyKeys, + __in LPCWSTR* propertyValues + ); + + +// function definitions + +HRESULT DnchostLoadRuntime( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzNativeHostPath, + __in LPCWSTR wzManagedHostPath, + __in LPCWSTR wzDepsJsonPath, + __in LPCWSTR wzRuntimeConfigPath + ) +{ + HRESULT hr = S_OK; + + hr = GetHostfxrPath(pState, wzNativeHostPath); + BalExitOnFailure(hr, "Failed to find hostfxr."); + + hr = LoadHostfxr(pState); + BalExitOnFailure(hr, "Failed to load hostfxr."); + + hr = InitializeHostfxr(pState, wzManagedHostPath, wzDepsJsonPath, wzRuntimeConfigPath); + BalExitOnFailure(hr, "Failed to initialize hostfxr."); + + hr = InitializeCoreClr(pState, wzNativeHostPath); + BalExitOnFailure(hr, "Failed to initialize coreclr."); + +LExit: + return hr; +} + +HRESULT DnchostCreateFactory( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzBaFactoryAssemblyName, + __in LPCWSTR wzBaFactoryAssemblyPath, + __out IBootstrapperApplicationFactory** ppAppFactory + ) +{ + HRESULT hr = S_OK; + PFNCREATEBAFACTORY pfnCreateBAFactory = NULL; + + if (pState->pfnGetFunctionPointer) + { + hr = pState->pfnGetFunctionPointer( + DNC_ENTRY_TYPEW, + DNC_STATIC_ENTRY_METHODW, + DNC_STATIC_ENTRY_DELEGATEW, + NULL, + NULL, + reinterpret_cast(&pfnCreateBAFactory)); + BalExitOnFailure(hr, "Failed to create delegate through GetFunctionPointer."); + } + else + { + hr = pState->pfnCoreclrCreateDelegate( + pState->pClrHandle, + pState->dwDomainId, + DNC_ASSEMBLY_FULL_NAME, + DNC_ENTRY_TYPE, + DNC_STATIC_ENTRY_METHOD, + reinterpret_cast(&pfnCreateBAFactory)); + BalExitOnFailure(hr, "Failed to create delegate in app domain."); + } + + *ppAppFactory = pfnCreateBAFactory(wzBaFactoryAssemblyName, wzBaFactoryAssemblyPath); + +LExit: + return hr; +} + +static HRESULT GetHostfxrPath( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzNativeHostPath + ) +{ + HRESULT hr = S_OK; + get_hostfxr_parameters getHostfxrParameters = { }; + int nrc = 0; + size_t cchHostFxrPath = MAX_PATH; + + getHostfxrParameters.size = sizeof(get_hostfxr_parameters); + getHostfxrParameters.assembly_path = wzNativeHostPath; + + // get_hostfxr_path does a full search on every call, so + // minimize the number of calls + // need to loop + for (;;) + { + cchHostFxrPath *= 2; + hr = StrAlloc(&pState->sczHostfxrPath, cchHostFxrPath); + BalExitOnFailure(hr, "Failed to allocate hostFxrPath."); + + nrc = get_hostfxr_path(pState->sczHostfxrPath, &cchHostFxrPath, &getHostfxrParameters); + if (HostApiBufferTooSmall != nrc) + { + break; + } + } + if (0 != nrc) + { + BalExitOnFailure(hr = nrc, "GetHostfxrPath failed"); + } + +LExit: + return hr; +} + +static HRESULT LoadHostfxr( + __in HOSTFXR_STATE* pState + ) +{ + HRESULT hr = S_OK; + HMODULE hHostfxr; + + hHostfxr = ::LoadLibraryExW(pState->sczHostfxrPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + BalExitOnNullWithLastError(hHostfxr, hr, "Failed to load hostfxr from '%ls'.", pState->sczHostfxrPath); + + pState->pfnHostfxrInitializeForApp = reinterpret_cast(::GetProcAddress(hHostfxr, "hostfxr_initialize_for_dotnet_command_line")); + BalExitOnNullWithLastError(pState->pfnHostfxrInitializeForApp, hr, "Failed to get procedure address for hostfxr_initialize_for_dotnet_command_line."); + + pState->pfnHostfxrGetRuntimeProperties = reinterpret_cast(::GetProcAddress(hHostfxr, "hostfxr_get_runtime_properties")); + BalExitOnNullWithLastError(pState->pfnHostfxrGetRuntimeProperties, hr, "Failed to get procedure address for hostfxr_get_runtime_properties."); + + pState->pfnHostfxrSetErrorWriter = reinterpret_cast(::GetProcAddress(hHostfxr, "hostfxr_set_error_writer")); + BalExitOnNullWithLastError(pState->pfnHostfxrSetErrorWriter, hr, "Failed to get procedure address for hostfxr_set_error_writer."); + + pState->pfnHostfxrClose = reinterpret_cast(::GetProcAddress(hHostfxr, "hostfxr_close")); + BalExitOnNullWithLastError(pState->pfnHostfxrClose, hr, "Failed to get procedure address for hostfxr_close."); + + pState->pfnHostfxrGetRuntimeDelegate = reinterpret_cast(::GetProcAddress(hHostfxr, "hostfxr_get_runtime_delegate")); + BalExitOnNullWithLastError(pState->pfnHostfxrGetRuntimeDelegate, hr, "Failed to get procedure address for hostfxr_get_runtime_delegate."); + +LExit: + // Never unload the module since it isn't meant to be unloaded. + + return hr; +} + +static void HOSTFXR_CALLTYPE DnchostErrorWriter( + __in LPCWSTR wzMessage + ) +{ + BOOTSTRAPPER_LOG_LEVEL level = BOOTSTRAPPER_LOG_LEVEL_ERROR; + + if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, 0, wzMessage, -1, L"The requested delegate type is not available in the target framework.", -1)) + { + level = BOOTSTRAPPER_LOG_LEVEL_DEBUG; + } + + BalLog(level, "error from hostfxr: %ls", wzMessage); +} + +static HRESULT InitializeHostfxr( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzManagedHostPath, + __in LPCWSTR wzDepsJsonPath, + __in LPCWSTR wzRuntimeConfigPath + ) +{ + HRESULT hr = S_OK; + + pState->pfnHostfxrSetErrorWriter(static_cast(&DnchostErrorWriter)); + + LPCWSTR argv[] = { + L"exec", + L"--depsfile", + wzDepsJsonPath, + L"--runtimeconfig", + wzRuntimeConfigPath, + wzManagedHostPath, + }; + hr = pState->pfnHostfxrInitializeForApp(sizeof(argv)/sizeof(LPWSTR), argv, NULL, &pState->hostContextHandle); + BalExitOnFailure(hr, "HostfxrInitializeForApp failed"); + +LExit: + return hr; +} + +static HRESULT InitializeCoreClr( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzNativeHostPath + ) +{ + HRESULT hr = S_OK; + + hr = pState->pfnHostfxrGetRuntimeDelegate(pState->hostContextHandle, hdt_get_function_pointer, reinterpret_cast(&pState->pfnGetFunctionPointer)); + if (InvalidArgFailure == hr || // old versions of hostfxr don't allow calling GetRuntimeDelegate from InitializeForApp. + HostApiUnsupportedVersion == hr) // hdt_get_function_pointer was added in .NET 5. + { + hr = InitializeCoreClrPre5(pState, wzNativeHostPath); + } + else + { + ExitOnFailure(hr, "HostfxrGetRuntimeDelegate failed"); + } + +LExit: + return hr; +} + +static HRESULT InitializeCoreClrPre5( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzNativeHostPath + ) +{ + HRESULT hr = S_OK; + int32_t rc = 0; + LPCWSTR* rgPropertyKeys = NULL; + LPCWSTR* rgPropertyValues = NULL; + size_t cProperties = 0; + LPWSTR* rgDirectories = NULL; + UINT cDirectories = 0; + LPWSTR sczCoreClrPath = NULL; + + // We are not using hostfxr as it was intended to be used. We need to initialize hostfxr so that it properly initializes hostpolicy - + // there are pieces of the framework such as AssemblyDependencyResolver that won't work without that. We also need hostfxr to find a + // compatible framework for framework-dependent deployed BAs. We had to use hostfxr_initialize_for_dotnet_command_line since + // hostfxr_initialize_for_runtime_config doesn't currently (3.x) support self-contained deployed BAs. That means we're supposed to + // start the runtime through hostfxr_run_app, but that method shuts down the runtime before returning. We actually want to call + // hostfxr_get_runtime_delegate, but that method currently requires hostfxr to be initialized through + // hostfxr_initialize_for_runtime_config. So we're forced to locate coreclr.dll and manually load the runtime ourselves. + + // Unfortunately, that's not the only problem. hostfxr has global state that tracks whether it started the runtime. While we keep our + // hostfxr_handle open, everyone that calls the hostfxr_initialize_* methods will block until we have started the runtime through + // hostfxr or closed our handle. If we close the handle, then hostfxr could potentially try to load a second runtime into the + // process, which is not supported. We're going to just keep our handle open since no one else in the process should be trying to + // start the runtime anyway. + + rc = pState->pfnHostfxrGetRuntimeProperties(pState->hostContextHandle, &cProperties, rgPropertyKeys, rgPropertyValues); + if (HostApiBufferTooSmall != rc) + { + BalExitOnFailure(hr = rc, "HostfxrGetRuntimeProperties failed to return required size."); + } + + rgPropertyKeys = static_cast(MemAlloc(sizeof(LPWSTR) * cProperties, TRUE)); + rgPropertyValues = static_cast(MemAlloc(sizeof(LPWSTR) * cProperties, TRUE)); + if (!rgPropertyKeys || !rgPropertyValues) + { + BalExitOnFailure(hr = E_OUTOFMEMORY, "Failed to allocate buffers for runtime properties."); + } + + hr = pState->pfnHostfxrGetRuntimeProperties(pState->hostContextHandle, &cProperties, rgPropertyKeys, rgPropertyValues); + BalExitOnFailure(hr, "HostfxrGetRuntimeProperties failed."); + + for (DWORD i = 0; i < cProperties; ++i) + { + if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, 0, rgPropertyKeys[i], -1, L"NATIVE_DLL_SEARCH_DIRECTORIES", -1)) + { + hr = StrSplitAllocArray(&rgDirectories, &cDirectories, rgPropertyValues[i], L";"); + BalExitOnFailure(hr, "Failed to split NATIVE_DLL_SEARCH_DIRECTORIES '%ls'", rgPropertyValues[i]); + } + } + + for (DWORD i = 0; i < cDirectories; ++i) + { + hr = PathConcat(rgDirectories[i], L"coreclr.dll", &sczCoreClrPath); + BalExitOnFailure(hr, "Failed to allocate path to coreclr."); + + if (::PathFileExists(sczCoreClrPath)) + { + break; + } + else + { + ReleaseNullStr(sczCoreClrPath); + } + } + + if (!sczCoreClrPath) + { + for (DWORD i = 0; i < cProperties; ++i) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "%ls: %ls", rgPropertyKeys[i], rgPropertyValues[i]); + } + BalExitOnFailure(hr = E_FILENOTFOUND, "Failed to locate coreclr.dll."); + } + + hr = LoadCoreClr(pState, sczCoreClrPath); + BalExitOnFailure(hr, "Failed to load coreclr."); + + hr = StartCoreClr(pState, wzNativeHostPath, (DWORD)cProperties, rgPropertyKeys, rgPropertyValues); + BalExitOnFailure(hr, "Failed to start coreclr."); + +LExit: + MemFree(rgDirectories); + MemFree(rgPropertyValues); + MemFree(rgPropertyKeys); + ReleaseStr(sczCoreClrPath); + + return hr; +} + +static HRESULT LoadCoreClr( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzCoreClrPath + ) +{ + HRESULT hr = S_OK; + HMODULE hModule = NULL; + + hModule = ::LoadLibraryExW(wzCoreClrPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + BalExitOnNullWithLastError(hModule, hr, "Failed to load coreclr.dll from '%ls'.", wzCoreClrPath); + + pState->pfnCoreclrInitialize = reinterpret_cast(::GetProcAddress(hModule, "coreclr_initialize")); + BalExitOnNullWithLastError(pState->pfnCoreclrInitialize, hr, "Failed to get procedure address for coreclr_initialize."); + + pState->pfnCoreclrCreateDelegate = reinterpret_cast(::GetProcAddress(hModule, "coreclr_create_delegate")); + BalExitOnNullWithLastError(pState->pfnCoreclrCreateDelegate, hr, "Failed to get procedure address for coreclr_create_delegate."); + +LExit: + // Never unload the module since coreclr doesn't support it. + + return hr; +} + +static HRESULT StartCoreClr( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzNativeHostPath, + __in DWORD cProperties, + __in LPCWSTR* propertyKeys, + __in LPCWSTR* propertyValues + ) +{ + HRESULT hr = S_OK; + LPSTR szNativeHostPath = NULL; + LPSTR* rgPropertyKeys = NULL; + LPSTR* rgPropertyValues = NULL; + + rgPropertyKeys = static_cast(MemAlloc(sizeof(LPSTR) * cProperties, TRUE)); + rgPropertyValues = static_cast(MemAlloc(sizeof(LPSTR) * cProperties, TRUE)); + if (!rgPropertyKeys || !rgPropertyValues) + { + BalExitOnFailure(hr = E_OUTOFMEMORY, "Failed to allocate buffers for runtime properties."); + } + + hr = StrAnsiAllocString(&szNativeHostPath, wzNativeHostPath, 0, CP_UTF8); + BalExitOnFailure(hr, "Failed to convert module path to UTF8: %ls", wzNativeHostPath); + + for (DWORD i = 0; i < cProperties; ++i) + { + hr = StrAnsiAllocString(&rgPropertyKeys[i], propertyKeys[i], 0, CP_UTF8); + BalExitOnFailure(hr, "Failed to convert property key to UTF8: %ls", propertyKeys[i]); + + hr = StrAnsiAllocString(&rgPropertyValues[i], propertyValues[i], 0, CP_UTF8); + BalExitOnFailure(hr, "Failed to convert property value to UTF8: %ls", propertyValues[i]); + } + + hr = pState->pfnCoreclrInitialize(szNativeHostPath, "MBA", cProperties, (LPCSTR*)rgPropertyKeys, (LPCSTR*)rgPropertyValues, &pState->pClrHandle, &pState->dwDomainId); + BalExitOnFailure(hr, "CoreclrInitialize failed."); + +LExit: + for (DWORD i = 0; i < cProperties; ++i) + { + if (rgPropertyKeys) + { + ReleaseStr(rgPropertyKeys[i]); + } + + if (rgPropertyValues) + { + ReleaseStr(rgPropertyValues[i]); + } + } + ReleaseMem(rgPropertyValues); + ReleaseMem(rgPropertyKeys); + ReleaseStr(szNativeHostPath); + + return hr; +} diff --git a/src/ext/Bal/dnchost/dncutil.h b/src/ext/Bal/dnchost/dncutil.h new file mode 100644 index 00000000..85eda3b2 --- /dev/null +++ b/src/ext/Bal/dnchost/dncutil.h @@ -0,0 +1,38 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +typedef IBootstrapperApplicationFactory* (STDMETHODCALLTYPE* PFNCREATEBAFACTORY)( + __in LPCWSTR wzBaFactoryAssemblyName, + __in LPCWSTR wzBaFactoryAssemblyPath + ); + +struct HOSTFXR_STATE +{ + LPWSTR sczHostfxrPath; + hostfxr_handle hostContextHandle; + hostfxr_initialize_for_dotnet_command_line_fn pfnHostfxrInitializeForApp; + hostfxr_get_runtime_properties_fn pfnHostfxrGetRuntimeProperties; + hostfxr_set_error_writer_fn pfnHostfxrSetErrorWriter; + hostfxr_close_fn pfnHostfxrClose; + hostfxr_get_runtime_delegate_fn pfnHostfxrGetRuntimeDelegate; + get_function_pointer_fn pfnGetFunctionPointer; + coreclr_initialize_ptr pfnCoreclrInitialize; + coreclr_create_delegate_ptr pfnCoreclrCreateDelegate; + void* pClrHandle; + UINT dwDomainId; +}; + +HRESULT DnchostLoadRuntime( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzNativeHostPath, + __in LPCWSTR wzManagedHostPath, + __in LPCWSTR wzDepsJsonPath, + __in LPCWSTR wzRuntimeConfigPath + ); + +HRESULT DnchostCreateFactory( + __in HOSTFXR_STATE* pState, + __in LPCWSTR wzBaFactoryAssemblyName, + __in LPCWSTR wzBaFactoryAssemblyPath, + __out IBootstrapperApplicationFactory** ppAppFactory + ); diff --git a/src/ext/Bal/dnchost/packages.config b/src/ext/Bal/dnchost/packages.config new file mode 100644 index 00000000..6c369364 --- /dev/null +++ b/src/ext/Bal/dnchost/packages.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ext/Bal/dnchost/precomp.cpp b/src/ext/Bal/dnchost/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/ext/Bal/dnchost/precomp.cpp @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" diff --git a/src/ext/Bal/dnchost/precomp.h b/src/ext/Bal/dnchost/precomp.h new file mode 100644 index 00000000..84ff6424 --- /dev/null +++ b/src/ext/Bal/dnchost/precomp.h @@ -0,0 +1,31 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#define NETHOST_USE_AS_STATIC +#include +#include +#include + +#include "coreclrhost.h" +#include "dncutil.h" +#include "dnchost.h" -- cgit v1.2.3-55-g6feb