Add mechanism for runtime to query host for information (#78798)
authorElinor Fung <elfung@microsoft.com>
Tue, 6 Dec 2022 23:23:40 +0000 (15:23 -0800)
committerGitHub <noreply@github.com>
Tue, 6 Dec 2022 23:23:40 +0000 (15:23 -0800)
14 files changed:
src/coreclr/dlls/mscoree/exports.cpp
src/coreclr/inc/hostinformation.h [new file with mode: 0644]
src/coreclr/vm/CMakeLists.txt
src/coreclr/vm/corhost.cpp
src/coreclr/vm/hostinformation.cpp [new file with mode: 0644]
src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostApiInvokerApp.csproj
src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostRuntimeContract.cs [new file with mode: 0644]
src/installer/tests/Assets/TestProjects/HostApiInvokerApp/Program.cs
src/installer/tests/HostActivation.Tests/NativeHostApis.cs
src/native/corehost/host_runtime_contract.h [new file with mode: 0644]
src/native/corehost/hostmisc/pal.h
src/native/corehost/hostmisc/pal.windows.cpp
src/native/corehost/hostpolicy/hostpolicy_context.cpp
src/native/corehost/hostpolicy/hostpolicy_context.h

index e8ee88275df8eba6db8f97625379341f3d50cd2a..1d9cc711e06e29b612578ef7269e1dc9ce84e117 100644 (file)
@@ -19,6 +19,8 @@
 #endif // FEATURE_GDBJIT
 #include "bundle.h"
 #include "pinvokeoverride.h"
+#include <hostinformation.h>
+#include <corehost/host_runtime_contract.h>
 
 #define ASSERTE_ALL_BUILDS(expr) _ASSERTE_ALL_BUILDS((expr))
 
@@ -122,7 +124,8 @@ static void ConvertConfigPropertiesToUnicode(
     LPCWSTR** propertyValuesWRef,
     BundleProbeFn** bundleProbe,
     PInvokeOverrideFn** pinvokeOverride,
-    bool* hostPolicyEmbedded)
+    bool* hostPolicyEmbedded,
+    host_runtime_contract** hostContract)
 {
     LPCWSTR* propertyKeysW = new (nothrow) LPCWSTR[propertyCount];
     ASSERTE_ALL_BUILDS(propertyKeysW != nullptr);
@@ -135,23 +138,43 @@ static void ConvertConfigPropertiesToUnicode(
         propertyKeysW[propertyIndex] = StringToUnicode(propertyKeys[propertyIndex]);
         propertyValuesW[propertyIndex] = StringToUnicode(propertyValues[propertyIndex]);
 
-        if (strcmp(propertyKeys[propertyIndex], "BUNDLE_PROBE") == 0)
+        if (strcmp(propertyKeys[propertyIndex], HOST_PROPERTY_BUNDLE_PROBE) == 0)
         {
             // If this application is a single-file bundle, the bundle-probe callback
             // is passed in as the value of "BUNDLE_PROBE" property (encoded as a string).
-            *bundleProbe = (BundleProbeFn*)_wcstoui64(propertyValuesW[propertyIndex], nullptr, 0);
+            // The function in HOST_RUNTIME_CONTRACT is given priority over this property,
+            // so we only set the bundle probe if it has not already been set.
+            if (*bundleProbe == nullptr)
+                *bundleProbe = (BundleProbeFn*)_wcstoui64(propertyValuesW[propertyIndex], nullptr, 0);
         }
-        else if (strcmp(propertyKeys[propertyIndex], "PINVOKE_OVERRIDE") == 0)
+        else if (strcmp(propertyKeys[propertyIndex], HOST_PROPERTY_PINVOKE_OVERRIDE) == 0)
         {
             // If host provides a PInvoke override (typically in a single-file bundle),
             // the override callback is passed in as the value of "PINVOKE_OVERRIDE" property (encoded as a string).
-            *pinvokeOverride = (PInvokeOverrideFn*)_wcstoui64(propertyValuesW[propertyIndex], nullptr, 0);
+            // The function in HOST_RUNTIME_CONTRACT is given priority over this property,
+            // so we only set the p/invoke override if it has not already been set.
+            if (*pinvokeOverride == nullptr)
+                *pinvokeOverride = (PInvokeOverrideFn*)_wcstoui64(propertyValuesW[propertyIndex], nullptr, 0);
         }
-        else if (strcmp(propertyKeys[propertyIndex], "HOSTPOLICY_EMBEDDED") == 0)
+        else if (strcmp(propertyKeys[propertyIndex], HOST_PROPERTY_HOSTPOLICY_EMBEDDED) == 0)
         {
             // The HOSTPOLICY_EMBEDDED property indicates if the executable has hostpolicy statically linked in
             *hostPolicyEmbedded = (wcscmp(propertyValuesW[propertyIndex], W("true")) == 0);
         }
+        else if (strcmp(propertyKeys[propertyIndex], HOST_PROPERTY_RUNTIME_CONTRACT) == 0)
+        {
+            // Host contract is passed in as the value of HOST_RUNTIME_CONTRACT property (encoded as a string).
+            host_runtime_contract* hostContractLocal = (host_runtime_contract*)_wcstoui64(propertyValuesW[propertyIndex], nullptr, 0);
+            *hostContract = hostContractLocal;
+
+            // Functions in HOST_RUNTIME_CONTRACT have priority over the individual properties
+            // for callbacks, so we set them as long as the contract has a non-null function.
+            if (hostContractLocal->bundle_probe != nullptr)
+                *bundleProbe = hostContractLocal->bundle_probe;
+
+            if (hostContractLocal->pinvoke_override != nullptr)
+                *pinvokeOverride = hostContractLocal->pinvoke_override;
+        }
     }
 
     *propertyKeysWRef = propertyKeysW;
@@ -196,6 +219,7 @@ int coreclr_initialize(
     BundleProbeFn* bundleProbe = nullptr;
     bool hostPolicyEmbedded = false;
     PInvokeOverrideFn* pinvokeOverride = nullptr;
+    host_runtime_contract* hostContract = nullptr;
 
     ConvertConfigPropertiesToUnicode(
         propertyKeys,
@@ -205,7 +229,8 @@ int coreclr_initialize(
         &propertyValuesW,
         &bundleProbe,
         &pinvokeOverride,
-        &hostPolicyEmbedded);
+        &hostPolicyEmbedded,
+        &hostContract);
 
 #ifdef TARGET_UNIX
     DWORD error = PAL_InitializeCoreCLR(exePath, g_coreclr_embedded);
@@ -221,6 +246,11 @@ int coreclr_initialize(
 
     g_hostpolicy_embedded = hostPolicyEmbedded;
 
+    if (hostContract != nullptr)
+    {
+        HostInformation::SetContract(hostContract);
+    }
+
     if (pinvokeOverride != nullptr)
     {
         PInvokeOverride::SetPInvokeOverride(pinvokeOverride, PInvokeOverride::Source::RuntimeConfiguration);
diff --git a/src/coreclr/inc/hostinformation.h b/src/coreclr/inc/hostinformation.h
new file mode 100644 (file)
index 0000000..d57b472
--- /dev/null
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef _HOSTINFORMATION_H_
+#define _HOSTINFORMATION_H_
+
+#include <corehost/host_runtime_contract.h>
+
+class HostInformation
+{
+public:
+    static void SetContract(_In_ host_runtime_contract* hostContract);
+    static bool GetProperty(_In_z_ const char* name, SString& value);
+};
+
+#endif // _HOSTINFORMATION_H_
index 68169ccb4edea263b7886247c54bf015cbf5e861..7d7826b5a0d70a9a568ff0cfb9f8c6a6502c489e 100644 (file)
@@ -328,6 +328,7 @@ set(VM_SOURCES_WKS
     genanalysis.cpp
     genmeth.cpp
     hosting.cpp
+    hostinformation.cpp
     ilmarshalers.cpp
     interopconverter.cpp
     interoputil.cpp
index 72fe94761c1dbe0cdd5b3187470c3af168982880..052e6a0480c18cdb101bc0cdfd13190a64714fb0 100644 (file)
@@ -38,6 +38,8 @@
 
 #ifndef DACCESS_COMPILE
 
+#include <corehost/host_runtime_contract.h>
+
 extern void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading);
 
 //***************************************************************************
@@ -578,22 +580,22 @@ HRESULT CorHost2::CreateAppDomainWithManager(
 
     for (int i = 0; i < nProperties; i++)
     {
-        if (wcscmp(pPropertyNames[i], W("NATIVE_DLL_SEARCH_DIRECTORIES")) == 0)
+        if (wcscmp(pPropertyNames[i], _T(HOST_PROPERTY_NATIVE_DLL_SEARCH_DIRECTORIES)) == 0)
         {
             pwzNativeDllSearchDirectories = pPropertyValues[i];
         }
         else
-        if (wcscmp(pPropertyNames[i], W("TRUSTED_PLATFORM_ASSEMBLIES")) == 0)
+        if (wcscmp(pPropertyNames[i], _T(HOST_PROPERTY_TRUSTED_PLATFORM_ASSEMBLIES)) == 0)
         {
             pwzTrustedPlatformAssemblies = pPropertyValues[i];
         }
         else
-        if (wcscmp(pPropertyNames[i], W("PLATFORM_RESOURCE_ROOTS")) == 0)
+        if (wcscmp(pPropertyNames[i], _T(HOST_PROPERTY_PLATFORM_RESOURCE_ROOTS)) == 0)
         {
             pwzPlatformResourceRoots = pPropertyValues[i];
         }
         else
-        if (wcscmp(pPropertyNames[i], W("APP_PATHS")) == 0)
+        if (wcscmp(pPropertyNames[i], _T(HOST_PROPERTY_APP_PATHS)) == 0)
         {
             pwzAppPaths = pPropertyValues[i];
         }
diff --git a/src/coreclr/vm/hostinformation.cpp b/src/coreclr/vm/hostinformation.cpp
new file mode 100644 (file)
index 0000000..b440f6f
--- /dev/null
@@ -0,0 +1,42 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+#include "hostinformation.h"
+
+namespace
+{
+    host_runtime_contract* s_hostContract = nullptr;
+}
+
+void HostInformation::SetContract(_In_ host_runtime_contract* hostContract)
+{
+    _ASSERTE(s_hostContract == nullptr);
+    s_hostContract = hostContract;
+}
+
+bool HostInformation::GetProperty(_In_z_ const char* name, SString& value)
+{
+    if (s_hostContract == nullptr || s_hostContract->get_runtime_property == nullptr)
+        return false;
+
+    size_t len = MAX_PATH + 1;
+    char* dest = value.OpenUTF8Buffer(static_cast<COUNT_T>(len));
+    size_t lenActual = s_hostContract->get_runtime_property(name, dest, len, s_hostContract->context);
+    value.CloseBuffer();
+
+    // Doesn't exist or failed to get property
+    if (lenActual == (size_t)-1 || lenActual == 0)
+        return false;
+
+    if (lenActual <= len)
+        return true;
+
+    // Buffer was not large enough
+    len = lenActual;
+    dest = value.OpenUTF8Buffer(static_cast<COUNT_T>(len));
+    lenActual = s_hostContract->get_runtime_property(name, dest, len, s_hostContract->context);
+    value.CloseBuffer();
+
+    return lenActual > 0 && lenActual <= len;
+}
index 087d0c77a536d8003223481b63b16ef67d092465..d040ee25bcfec7751925a902237d4b79c89beeed 100644 (file)
@@ -5,10 +5,7 @@
     <OutputType>Exe</OutputType>
     <RuntimeFrameworkVersion>$(MNAVersion)</RuntimeFrameworkVersion>
     <DefineConstants Condition="'$(OS)' == 'Windows_NT'">WINDOWS;$(DefineConstants)</DefineConstants>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
 
-  <ItemGroup>
-    <PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
-  </ItemGroup>
-
 </Project>
diff --git a/src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostRuntimeContract.cs b/src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostRuntimeContract.cs
new file mode 100644 (file)
index 0000000..a2a4d61
--- /dev/null
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace HostApiInvokerApp
+{
+    public static unsafe class HostRuntimeContract
+    {
+        internal struct host_runtime_contract
+        {
+            public nint size;
+            public void* context;
+            public delegate* unmanaged[Stdcall]<byte*, byte*, nint, void*, nint> get_runtime_property;
+            public IntPtr bundle_probe;
+            public IntPtr pinvoke_override;
+        }
+
+        private static host_runtime_contract GetContract()
+        {
+            string contractString = (string)AppContext.GetData("HOST_RUNTIME_CONTRACT");
+            if (string.IsNullOrEmpty(contractString))
+                throw new Exception("HOST_RUNTIME_CONTRACT not found");
+
+            host_runtime_contract* contract = (host_runtime_contract*)Convert.ToUInt64(contractString, 16);
+            if (contract->size != sizeof(host_runtime_contract))
+                throw new Exception($"Unexpected contract size {contract->size}. Expected: {sizeof(host_runtime_contract)}");
+
+            return *contract;
+        }
+
+        private static void Test_get_runtime_property(string[] args)
+        {
+            host_runtime_contract contract = GetContract();
+
+            foreach (string name in args)
+            {
+                string value = GetProperty(name, contract);
+                Console.WriteLine($"{nameof(host_runtime_contract.get_runtime_property)}: {name} = {(value == null ? "<none>" : value)}");
+            }
+
+            static string GetProperty(string name, host_runtime_contract contract)
+            {
+                Span<byte> nameSpan = stackalloc byte[Encoding.UTF8.GetMaxByteCount(name.Length)];
+                byte* namePtr = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(nameSpan));
+                int nameLen = Encoding.UTF8.GetBytes(name, nameSpan);
+                nameSpan[nameLen] = 0;
+
+                nint len = 256;
+                byte* buffer = stackalloc byte[(int)len];
+                nint lenActual = contract.get_runtime_property(namePtr, buffer, len, contract.context);
+                if (lenActual <= 0)
+                {
+                    Console.WriteLine($"No value for {name} - {nameof(host_runtime_contract.get_runtime_property)} returned {lenActual}");
+                    return null;
+                }
+
+                if (lenActual <= len)
+                    return Encoding.UTF8.GetString(buffer, (int)lenActual);
+
+                len = lenActual;
+                byte* expandedBuffer = stackalloc byte[(int)len];
+                lenActual = contract.get_runtime_property(namePtr, expandedBuffer, len, contract.context);
+                return Encoding.UTF8.GetString(expandedBuffer, (int)lenActual);
+            }
+        }
+
+        public static bool RunTest(string apiToTest, string[] args)
+        {
+            switch (apiToTest)
+            {
+                case $"{nameof(host_runtime_contract)}.{nameof(host_runtime_contract.get_runtime_property)}":
+                    Test_get_runtime_property(args);
+                    break;
+                default:
+                    return false;
+            }
+
+            return true;
+        }
+    }
+
+}
index 1455a80a358df356a61527a931829e4fe2061b57..2831ed8c3d4878019d5c2022972bfc55b889751b 100644 (file)
@@ -31,16 +31,13 @@ namespace HostApiInvokerApp
             Console.WriteLine("Hello World!");
             Console.WriteLine(string.Join(Environment.NewLine, args));
 
-            // A small operation involving NewtonSoft.Json to ensure the assembly is loaded properly
-            var t = typeof(Newtonsoft.Json.JsonReader);
-
             // Enable tracing so that test assertion failures are easier to diagnose.
             Environment.SetEnvironmentVariable("COREHOST_TRACE", "1");
 
             // If requested, test multilevel lookup using fake Global SDK directories:
             //     1. using a fake ProgramFiles location
             //     2. using a fake SDK Self-Registered location
-            // Note that this has to be set here and not in the calling test process because 
+            // Note that this has to be set here and not in the calling test process because
             // %ProgramFiles% gets reset on process creation.
             string testMultilevelLookupProgramFiles = Environment.GetEnvironmentVariable("TEST_MULTILEVEL_LOOKUP_PROGRAM_FILES");
             string testMultilevelLookupSelfRegistered = Environment.GetEnvironmentVariable("TEST_MULTILEVEL_LOOKUP_SELF_REGISTERED");
@@ -65,17 +62,15 @@ namespace HostApiInvokerApp
 
             string apiToTest = args[0];
             if (HostFXR.RunTest(apiToTest, args))
-            {
                 return;
-            }
-            else if (HostPolicy.RunTest(apiToTest, args))
-            {
+
+            if (HostPolicy.RunTest(apiToTest, args))
                 return;
-            }
-            else
-            {
-                throw new ArgumentException($"Invalid API to test passed as args[0]): {apiToTest}");
-            }
+
+            if (HostRuntimeContract.RunTest(apiToTest, args))
+                return;
+
+            throw new ArgumentException($"Invalid API to test passed as args[0]): {apiToTest}");
         }
     }
 }
index 213b3790335ce4759e4667cf7ff05df7ac315645..1649b2afeb33f2c80f3fc44ef8e68a36f01e51ff 100644 (file)
@@ -473,6 +473,20 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
                 .Should().Pass();
         }
 
+        [Fact]
+        public void HostRuntimeContract_get_runtime_property()
+        {
+            var fixture = sharedTestState.HostApiInvokerAppFixture;
+
+            fixture.BuiltDotnet.Exec(fixture.TestProject.AppDll, "host_runtime_contract.get_runtime_property", "APP_CONTEXT_BASE_DIRECTORY", "DOES_NOT_EXIST")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should().Pass()
+                .And.HaveStdOutContaining($"APP_CONTEXT_BASE_DIRECTORY = {Path.GetDirectoryName(fixture.TestProject.AppDll)}")
+                .And.HaveStdOutContaining($"DOES_NOT_EXIST = <none>");
+        }
+
         public class SharedTestState : IDisposable
         {
             public TestProjectFixture HostApiInvokerAppFixture { get; }
diff --git a/src/native/corehost/host_runtime_contract.h b/src/native/corehost/host_runtime_contract.h
new file mode 100644 (file)
index 0000000..919766e
--- /dev/null
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef __HOST_RUNTIME_CONTRACT_H__
+#define __HOST_RUNTIME_CONTRACT_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(_WIN32)
+    #define HOST_CONTRACT_CALLTYPE __stdcall
+#else
+    #define HOST_CONTRACT_CALLTYPE
+#endif
+
+// Known host property names
+#define HOST_PROPERTY_RUNTIME_CONTRACT "HOST_RUNTIME_CONTRACT"
+#define HOST_PROPERTY_APP_PATHS "APP_PATHS"
+#define HOST_PROPERTY_BUNDLE_PROBE "BUNDLE_PROBE"
+#define HOST_PROPERTY_HOSTPOLICY_EMBEDDED "HOSTPOLICY_EMBEDDED"
+#define HOST_PROPERTY_NATIVE_DLL_SEARCH_DIRECTORIES "NATIVE_DLL_SEARCH_DIRECTORIES"
+#define HOST_PROPERTY_PINVOKE_OVERRIDE "PINVOKE_OVERRIDE"
+#define HOST_PROPERTY_PLATFORM_RESOURCE_ROOTS "PLATFORM_RESOURCE_ROOTS"
+#define HOST_PROPERTY_TRUSTED_PLATFORM_ASSEMBLIES "TRUSTED_PLATFORM_ASSEMBLIES"
+
+struct host_runtime_contract
+{
+    size_t size;
+
+    // Context for the contract. Pass to functions taking a contract context.
+    void* context;
+
+    // Get the value of a runtime property.
+    // Returns the length of the property including a terminating null or -1 if not found.
+    size_t(HOST_CONTRACT_CALLTYPE* get_runtime_property)(
+        const char* key,
+        /*out*/ char* value_buffer,
+        size_t value_buffer_size,
+        void* contract_context);
+
+    // Probe an app bundle for `path`. Sets its location (`offset`, `size`) in the bundle if found.
+    // Returns true if found, false otherwise.
+    bool(HOST_CONTRACT_CALLTYPE* bundle_probe)(
+        const char* path,
+        /*out*/ int64_t* offset,
+        /*out*/ int64_t* size,
+        /*out*/ int64_t* compressedSize);
+
+    // Get the function overriding the specified p/invoke (`library_name`, `entry_point_name`).
+    // Returns a pointer to the function if the p/invoke is overridden, nullptr otherwise.
+    const void* (HOST_CONTRACT_CALLTYPE* pinvoke_override)(
+        const char* library_name,
+        const char* entry_point_name);
+};
+
+#endif // __HOST_RUNTIME_CONTRACT_H__
index 2af1dbd214fd6618bce68f51d22cb087aa04a70d..dee5bf3c94d444596f4eeb199e1be48637810237 100644 (file)
@@ -180,6 +180,7 @@ namespace pal
         return buffer;
     }
 
+    size_t pal_utf8string(const string_t& str, char* out_buffer, size_t len);
     bool pal_utf8string(const string_t& str, std::vector<char>* out);
     bool pal_clrstring(const string_t& str, std::vector<char>* out);
     bool clr_palstring(const char* cstr, string_t* out);
@@ -236,6 +237,16 @@ namespace pal
 
     inline const string_t strerror(int errnum) { return ::strerror(errnum); }
 
+    inline size_t pal_utf8string(const string_t& str, char* out_buffer, size_t buffer_len)
+    {
+        size_t len = str.size() + 1;
+        if (buffer_len < len)
+            return len;
+
+        ::strncpy(out_buffer, str.c_str(), str.size());
+        out_buffer[len - 1] = '\0';
+        return len;
+    }
     inline bool pal_utf8string(const string_t& str, std::vector<char>* out) { out->assign(str.begin(), str.end()); out->push_back('\0'); return true; }
     inline bool pal_clrstring(const string_t& str, std::vector<char>* out) { return pal_utf8string(str, out); }
     inline bool clr_palstring(const char* cstr, string_t* out) { out->assign(cstr); return true; }
index bbb6fabb30b79f0801bf071ce44f722ea1cf0dc2..4dd632ac88d336e9ea278061d940dd5257d0f5ce 100644 (file)
@@ -689,6 +689,17 @@ static bool wchar_convert_helper(DWORD code_page, const char* cstr, size_t len,
     return ::MultiByteToWideChar(code_page, 0, cstr, static_cast<uint32_t>(len), &(*out)[0], static_cast<uint32_t>(out->size())) != 0;
 }
 
+size_t pal::pal_utf8string(const pal::string_t& str, char* out_buffer, size_t len)
+{
+    // Pass -1 as we want explicit null termination in the char buffer.
+    size_t size = ::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, nullptr, 0, nullptr, nullptr);
+    if (size == 0 || size > len)
+        return size;
+
+    // Pass -1 as we want explicit null termination in the char buffer.
+    return ::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, out_buffer, static_cast<uint32_t>(len), nullptr, nullptr);
+}
+
 bool pal::pal_utf8string(const pal::string_t& str, std::vector<char>* out)
 {
     out->clear();
index e1cff0dc376efa55b5fd605f891928d8fb60d009..7d27951ed226fd2b84941f65c424164c77526019 100644 (file)
@@ -2,7 +2,8 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 #include "hostpolicy_context.h"
-#include "hostpolicy.h"
+#include <hostpolicy.h>
+#include <host_runtime_contract.h>
 
 #include "deps_resolver.h"
 #include <error_codes.h>
@@ -104,6 +105,27 @@ namespace
         return nullptr;
     }
 #endif
+
+    size_t HOST_CONTRACT_CALLTYPE get_runtime_property(
+        const char* key,
+        char* value_buffer,
+        size_t value_buffer_size,
+        void* contract_context)
+    {
+        hostpolicy_context_t* context = static_cast<hostpolicy_context_t*>(contract_context);
+
+        pal::string_t key_str;
+        if (pal::clr_palstring(key, &key_str))
+        {
+            const pal::char_t* value;
+            if (context->coreclr_properties.try_get(key_str.c_str(), &value))
+            {
+                return pal::pal_utf8string(value, value_buffer, value_buffer_size);
+            }
+        }
+
+        return -1;
+    }
 }
 
 int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const arguments_t &args, bool enable_breadcrumbs)
@@ -324,5 +346,25 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a
     }
 #endif
 
+    {
+        host_contract = { sizeof(host_runtime_contract), this };
+        if (bundle::info_t::is_single_file_bundle())
+        {
+            host_contract.bundle_probe = &bundle_probe;
+#if defined(NATIVE_LIBS_EMBEDDED)
+            host_contract.pinvoke_override = &pinvoke_override;
+#endif
+        }
+
+        host_contract.get_runtime_property = &get_runtime_property;
+        pal::stringstream_t ptr_stream;
+        ptr_stream << "0x" << std::hex << (size_t)(&host_contract);
+        if (!coreclr_properties.add(_STRINGIFY(HOST_PROPERTY_RUNTIME_CONTRACT), ptr_stream.str().c_str()))
+        {
+            log_duplicate_property_error(_STRINGIFY(HOST_PROPERTY_RUNTIME_CONTRACT));
+            return StatusCode::LibHostDuplicateProperty;
+        }
+    }
+
     return StatusCode::Success;
 }
index 2853e6ae98fb16a5a7a1453261436d1e0f7c607a..bec4630762fe03b2bda94e4dbfe6532df55f3ced 100644 (file)
@@ -9,6 +9,7 @@
 #include "args.h"
 #include "coreclr.h"
 #include <corehost_context_contract.h>
+#include <host_runtime_contract.h>
 #include "hostpolicy_init.h"
 
 struct hostpolicy_context_t
@@ -27,6 +28,8 @@ public:
 
     std::unique_ptr<coreclr_t> coreclr;
 
+    host_runtime_contract host_contract;
+
     int initialize(hostpolicy_init_t &hostpolicy_init, const arguments_t &args, bool enable_breadcrumbs);
 };