#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))
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);
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;
BundleProbeFn* bundleProbe = nullptr;
bool hostPolicyEmbedded = false;
PInvokeOverrideFn* pinvokeOverride = nullptr;
+ host_runtime_contract* hostContract = nullptr;
ConvertConfigPropertiesToUnicode(
propertyKeys,
&propertyValuesW,
&bundleProbe,
&pinvokeOverride,
- &hostPolicyEmbedded);
+ &hostPolicyEmbedded,
+ &hostContract);
#ifdef TARGET_UNIX
DWORD error = PAL_InitializeCoreCLR(exePath, g_coreclr_embedded);
g_hostpolicy_embedded = hostPolicyEmbedded;
+ if (hostContract != nullptr)
+ {
+ HostInformation::SetContract(hostContract);
+ }
+
if (pinvokeOverride != nullptr)
{
PInvokeOverride::SetPInvokeOverride(pinvokeOverride, PInvokeOverride::Source::RuntimeConfiguration);
--- /dev/null
+// 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_
genanalysis.cpp
genmeth.cpp
hosting.cpp
+ hostinformation.cpp
ilmarshalers.cpp
interopconverter.cpp
interoputil.cpp
#ifndef DACCESS_COMPILE
+#include <corehost/host_runtime_contract.h>
+
extern void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading);
//***************************************************************************
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];
}
--- /dev/null
+// 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;
+}
<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>
--- /dev/null
+// 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;
+ }
+ }
+
+}
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");
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}");
}
}
}
.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; }
--- /dev/null
+// 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__
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);
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; }
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();
// 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>
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)
}
#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;
}
#include "args.h"
#include "coreclr.h"
#include <corehost_context_contract.h>
+#include <host_runtime_contract.h>
#include "hostpolicy_init.h"
struct hostpolicy_context_t
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);
};