Skip to content

Commit 2485745

Browse files
authored
[release/8.0-staging] Add runtime config parameter to force ijwhost to load assemblies in an isolated context (#105436)
1 parent 4c77671 commit 2485745

File tree

5 files changed

+65
-14
lines changed

5 files changed

+65
-14
lines changed

Diff for: src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private static unsafe void LoadInMemoryAssemblyInContextWhenSupported(IntPtr mod
4949
/// </summary>
5050
/// <param name="moduleHandle">The native module handle for the assembly.</param>
5151
/// <param name="assemblyPath">The path to the assembly (as a pointer to a UTF-16 C string).</param>
52-
/// <param name="loadContext">Load context (currently must be IntPtr.Zero)</param>
52+
/// <param name="loadContext">Load context (currently must be either IntPtr.Zero for default ALC or -1 for isolated ALC)</param>
5353
[UnmanagedCallersOnly]
5454
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
5555
Justification = "The same C++/CLI feature switch applies to LoadInMemoryAssembly and this function. We rely on the warning from LoadInMemoryAssembly.")]
@@ -58,9 +58,12 @@ public static unsafe void LoadInMemoryAssemblyInContext(IntPtr moduleHandle, Int
5858
if (!IsSupported)
5959
throw new NotSupportedException(SR.NotSupported_CppCli);
6060

61-
ArgumentOutOfRangeException.ThrowIfNotEqual(loadContext, IntPtr.Zero);
61+
if ((loadContext != IntPtr.Zero) && (loadContext != -1))
62+
{
63+
throw new ArgumentOutOfRangeException(nameof(loadContext));
64+
}
6265

63-
LoadInMemoryAssemblyInContextImpl(moduleHandle, assemblyPath, AssemblyLoadContext.Default);
66+
LoadInMemoryAssemblyInContextImpl(moduleHandle, assemblyPath, (loadContext == IntPtr.Zero) ? AssemblyLoadContext.Default : null);
6467
}
6568

6669
[RequiresUnreferencedCode("C++/CLI is not trim-compatible", Url = "https://aka.ms/dotnet-illink/nativehost")]

Diff for: src/installer/tests/HostActivation.Tests/NativeHosting/Ijwhost.cs

+40-6
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public void LoadLibrary()
2727
{
2828
string [] args = {
2929
"ijwhost",
30-
sharedState.IjwLibraryPath,
30+
sharedState.IjwApp.AppDll,
3131
"NativeEntryPoint"
3232
};
3333
CommandResult result = sharedState.CreateNativeHostCommand(args, sharedState.RepoDirectories.BuiltDotnet)
@@ -38,14 +38,49 @@ public void LoadLibrary()
3838
.And.HaveStdOutContaining("[C++/CLI] ManagedClass: AssemblyLoadContext = \"Default\" System.Runtime.Loader.DefaultAssemblyLoadContext");
3939
}
4040

41+
[Theory]
42+
[InlineData(true)]
43+
[InlineData(false)]
44+
public void LoadLibrary_ContextConfig(bool load_isolated)
45+
{
46+
// make a copy of a portion of the shared state because we will modify it
47+
using (var app = sharedState.IjwApp.Copy())
48+
{
49+
string[] args = {
50+
"ijwhost",
51+
app.AppDll,
52+
"NativeEntryPoint"
53+
};
54+
55+
RuntimeConfig.FromFile(app.RuntimeConfigJson)
56+
.WithProperty("System.Runtime.InteropServices.CppCLI.LoadComponentInIsolatedContext", load_isolated.ToString())
57+
.Save();
58+
59+
CommandResult result = sharedState.CreateNativeHostCommand(args, sharedState.RepoDirectories.BuiltDotnet)
60+
.Execute();
61+
62+
result.Should().Pass()
63+
.And.HaveStdOutContaining("[C++/CLI] NativeEntryPoint: calling managed class");
64+
65+
if (load_isolated) // Assembly should be loaded in an isolated context
66+
{
67+
result.Should().HaveStdOutContaining("[C++/CLI] ManagedClass: AssemblyLoadContext = \"IsolatedComponentLoadContext");
68+
}
69+
else // Assembly should be loaded in the default context
70+
{
71+
result.Should().HaveStdOutContaining("[C++/CLI] ManagedClass: AssemblyLoadContext = \"Default\" System.Runtime.Loader.DefaultAssemblyLoadContext");
72+
}
73+
}
74+
}
75+
4176
[Theory]
4277
[InlineData(true)]
4378
[InlineData(false)]
4479
public void ManagedHost(bool selfContained)
4580
{
4681
string [] args = {
4782
"ijwhost",
48-
sharedState.IjwLibraryPath,
83+
sharedState.IjwApp.AppDll,
4984
"NativeEntryPoint"
5085
};
5186
TestProjectFixture fixture = selfContained ? sharedState.ManagedHostFixture_SelfContained : sharedState.ManagedHostFixture_FrameworkDependent;
@@ -63,24 +98,23 @@ public void ManagedHost(bool selfContained)
6398

6499
public class SharedTestState : SharedTestStateBase
65100
{
66-
public string IjwLibraryPath { get; }
101+
public TestApp IjwApp {get;}
67102

68103
public TestProjectFixture ManagedHostFixture_FrameworkDependent { get; }
69104
public TestProjectFixture ManagedHostFixture_SelfContained { get; }
70105

71106
public SharedTestState()
72107
{
73108
string folder = Path.Combine(BaseDirectory, "ijw");
74-
Directory.CreateDirectory(folder);
109+
IjwApp = new TestApp(folder, "ijw");
75110

76111
// Copy over ijwhost
77112
string ijwhostName = "ijwhost.dll";
78113
File.Copy(Path.Combine(RepoDirectories.HostArtifacts, ijwhostName), Path.Combine(folder, ijwhostName));
79114

80115
// Copy over the C++/CLI test library
81116
string ijwLibraryName = "ijw.dll";
82-
IjwLibraryPath = Path.Combine(folder, ijwLibraryName);
83-
File.Copy(Path.Combine(RepoDirectories.HostTestArtifacts, ijwLibraryName), IjwLibraryPath);
117+
File.Copy(Path.Combine(RepoDirectories.HostTestArtifacts, ijwLibraryName), Path.Combine(folder, ijwLibraryName));
84118

85119
// Create a runtimeconfig.json for the C++/CLI test library
86120
new RuntimeConfig(Path.Combine(folder, "ijw.runtimeconfig.json"))

Diff for: src/native/corehost/ijwhost/ijwhost.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#define IJW_API SHARED_API
2121
#endif // _WIN32
2222

23-
pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_memory_assembly_fn* delegate)
23+
pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_memory_assembly_fn* delegate, void **load_context)
2424
{
2525
get_function_pointer_fn get_function_pointer;
2626
int status = load_fxr_and_get_delegate(
@@ -40,7 +40,17 @@ pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_m
4040

4141
return StatusCode::Success;
4242
},
43-
[](pal::dll_t fxr, hostfxr_handle context){ },
43+
[load_context](pal::dll_t fxr, hostfxr_handle context)
44+
{
45+
*load_context = nullptr; // default load context
46+
auto get_runtime_property_value = reinterpret_cast<hostfxr_get_runtime_property_value_fn>(pal::get_symbol(fxr, "hostfxr_get_runtime_property_value"));
47+
const pal::char_t* value;
48+
if (get_runtime_property_value(context, _X("System.Runtime.InteropServices.CppCLI.LoadComponentInIsolatedContext"), &value) == StatusCode::Success
49+
&& pal::strcasecmp(value, _X("true")) == 0)
50+
{
51+
*load_context = ISOLATED_CONTEXT; // Isolated load context
52+
}
53+
},
4454
reinterpret_cast<void**>(&get_function_pointer)
4555
);
4656
if (status != StatusCode::Success)

Diff for: src/native/corehost/ijwhost/ijwhost.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ bool are_thunks_installed_for_module(HMODULE instance);
1515

1616
using load_in_memory_assembly_fn = void(STDMETHODCALLTYPE*)(pal::dll_t handle, const pal::char_t* path, void* load_context);
1717

18-
pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_memory_assembly_fn* delegate);
18+
pal::hresult_t get_load_in_memory_assembly_delegate(pal::dll_t handle, load_in_memory_assembly_fn* delegate, void **load_context);
1919

2020
extern HANDLE g_heapHandle;
2121

22+
#define ISOLATED_CONTEXT (void*)-1
23+
2224
#endif

Diff for: src/native/corehost/ijwhost/ijwthunk.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ extern "C" std::uintptr_t __stdcall start_runtime_and_get_target_address(std::ui
119119
bootstrap_thunk *pThunk = bootstrap_thunk::get_thunk_from_cookie(cookie);
120120
load_in_memory_assembly_fn loadInMemoryAssembly;
121121
pal::dll_t moduleHandle = pThunk->get_dll_handle();
122-
pal::hresult_t status = get_load_in_memory_assembly_delegate(moduleHandle, &loadInMemoryAssembly);
122+
123+
void* load_context = nullptr;
124+
pal::hresult_t status = get_load_in_memory_assembly_delegate(moduleHandle, &loadInMemoryAssembly, &load_context);
123125

124126
if (status != StatusCode::Success)
125127
{
@@ -145,7 +147,7 @@ extern "C" std::uintptr_t __stdcall start_runtime_and_get_target_address(std::ui
145147
#pragma warning (pop)
146148
}
147149

148-
loadInMemoryAssembly(moduleHandle, app_path.c_str(), nullptr);
150+
loadInMemoryAssembly(moduleHandle, app_path.c_str(), load_context);
149151

150152
std::uintptr_t thunkAddress = *(pThunk->get_slot_address());
151153

0 commit comments

Comments
 (0)