Skip to content

Commit b7224e5

Browse files
authored
[One .NET] Enable support for AssemblyLoadContext (#5940)
Context: https://docs.microsoft.com/dotnet/api/system.runtime.loader.assemblyloadcontext?view=net-5.0 .NET Core -- and thus, .NET 5+ -- removed most support for `System.AppDomain` -- technically, there still exists a single `AppDomain`, but creation of new ones is no longer possible -- with [`System.Runtime.Loader.AssemblyLoadContext`][0] acting as the replacement for *some* previous `AppDomain` functionality. TL;DR: `AssemblyLoadContext` allows (potentially) loading and unloading assemblies, but *doesn't* allow creating an in-process "sandbox" like `AppDomain` originally did. ([Code Access Security][1] was deprecated by .NET Framework 4 and [isn't present in .NET 5][2]; `AppDomain` for sandboxing purposes was, in retrospect, rarely a good idea.) Commit 0cd890b introduced partial support for using `AssemblyLoadContext`, but it was necessarily incomplete until after [dotnet/runtime#53308][3] and other fixes landed. Add support for calling the new `AssemblyLoadContext`-oriented MonoVM functions to load an assembly into either the default `AssemblyLoadContext` (early in the startup process) or into the application-created context later on during application run time. MonoVM also adds new preload hooks which work with the `AssemblyLoadContext` instead of the older AppDomains. [0]: https://docs.microsoft.com/dotnet/api/system.runtime.loader.assemblyloadcontext?view=net-5.0 [1]: https://docs.microsoft.com/previous-versions/dotnet/framework/code-access-security/code-access-security [2]: https://docs.microsoft.com/dotnet/core/compatibility/core-libraries/5.0/code-access-security-apis-obsolete#reason-for-change [3]: dotnet/runtime#53308
1 parent 4a7514c commit b7224e5

File tree

6 files changed

+183
-75
lines changed

6 files changed

+183
-75
lines changed

src/monodroid/jni/embedded-assemblies.cc

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,45 @@ EmbeddedAssemblies::get_assembly_data (const MonoBundledAssembly *e, char*& asse
130130
}
131131
}
132132

133+
#if defined (NET6)
133134
MonoAssembly*
134-
#if defined (NET6) && defined (NET6_ALC_WORKS)
135-
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, MonoError *error)
136-
#else // !(def NET6 && def NET6_ALC_WORKS)
137-
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
138-
#endif // def NET6 && def NET6_ALC_WORKS
135+
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, [[maybe_unused]] MonoError *error)
139136
{
140-
#if defined (NET6)
141-
// NET6 doesn't support reference-only loads, define the variable here to minimize ifdefs in the code below
142-
#if defined (NET6_ALC_WORKS)
143-
constexpr bool ref_only = false;
144-
#else // def NET6_ALC_WORKS
145-
ref_only = false;
146-
#endif // ndef NET6_ALC_WORKS
137+
auto loader = [&] (char *assembly_data, uint32_t assembly_data_size, const char *name) -> MonoImage* {
138+
return mono_image_open_from_data_alc (
139+
alc_gchandle,
140+
assembly_data,
141+
assembly_data_size,
142+
0 /* need_copy */,
143+
nullptr /* status */,
144+
name
145+
);
146+
};
147+
148+
return open_from_bundles (aname, loader, false /* ref_only */);
149+
}
147150
#endif // def NET6
151+
152+
MonoAssembly*
153+
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
154+
{
155+
auto loader = [&] (char *assembly_data, uint32_t assembly_data_size, const char *name) -> MonoImage* {
156+
return mono_image_open_from_data_with_name (
157+
assembly_data,
158+
assembly_data_size,
159+
0,
160+
nullptr,
161+
ref_only,
162+
name
163+
);
164+
};
165+
166+
return open_from_bundles (aname, loader, ref_only);
167+
}
168+
169+
MonoAssembly*
170+
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, std::function<MonoImage*(char*, uint32_t, const char*)> loader, bool ref_only)
171+
{
148172
const char *culture = mono_assembly_name_get_culture (aname);
149173
const char *asmname = mono_assembly_name_get_name (aname);
150174

@@ -185,19 +209,7 @@ EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
185209
}
186210

187211
get_assembly_data (e, assembly_data, assembly_data_size);
188-
189-
#if defined (NET6) && defined (NET6_ALC_WORKS)
190-
image = mono_image_open_from_data_alc (
191-
alc_gchandle,
192-
assembly_data,
193-
assembly_data_size,
194-
0 /* need_copy */,
195-
nullptr /* status */,
196-
name.get ()
197-
);
198-
#else // (def NET6 && def NET6_ALC_WORKS)
199-
image = mono_image_open_from_data_with_name (assembly_data, assembly_data_size, 0, nullptr, ref_only, name.get ());
200-
#endif // !(def NET6 && def NET6_ALC_WORKS)
212+
image = loader (assembly_data, assembly_data_size, name.get ());
201213
if (image == nullptr) {
202214
continue;
203215
}
@@ -215,51 +227,49 @@ EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
215227
log_info_nocheck (LOG_ASSEMBLY, "open_from_bundles: loaded assembly: %p\n", a);
216228
}
217229

218-
#if defined (NET6) && defined (NET6_ALC_WORKS)
219-
if (error != nullptr) {
220-
error->error_code = a == nullptr ? MONO_ERROR_NONE : MONO_ERROR_FILE_NOT_FOUND;
221-
}
222-
#endif // def NET6 && def NET6_ALC_WORKS
223230
return a;
224231
}
225232

226-
#if defined (NET6) && defined (NET6_ALC_WORKS)
233+
#if defined (NET6)
227234
MonoAssembly*
228235
EmbeddedAssemblies::open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data, MonoError *error)
229236
{
230-
log_warn (LOG_DEFAULT, __PRETTY_FUNCTION__);
231237
return embeddedAssemblies.open_from_bundles (aname, alc_gchandle, error);
232238
}
233-
#else // def NET6 && def NET6_ALC_WORKS
239+
#else // def NET6
240+
MonoAssembly*
241+
EmbeddedAssemblies::open_from_bundles_refonly (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data)
242+
{
243+
return embeddedAssemblies.open_from_bundles (aname, true);
244+
}
245+
#endif // ndef NET6
246+
234247
MonoAssembly*
235248
EmbeddedAssemblies::open_from_bundles_full (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data)
236249
{
237250
return embeddedAssemblies.open_from_bundles (aname, false);
238251
}
239252

240-
MonoAssembly*
241-
EmbeddedAssemblies::open_from_bundles_refonly (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data)
253+
void
254+
EmbeddedAssemblies::install_preload_hooks_for_appdomains ()
242255
{
243-
return embeddedAssemblies.open_from_bundles (aname, true);
256+
mono_install_assembly_preload_hook (open_from_bundles_full, nullptr);
257+
#if !defined (NET6)
258+
mono_install_assembly_refonly_preload_hook (open_from_bundles_refonly, nullptr);
259+
#endif // ndef NET6
244260
}
245-
#endif // !(def NET6 && def NET6_ALC_WORKS)
246261

262+
#if defined (NET6)
247263
void
248-
EmbeddedAssemblies::install_preload_hooks ()
264+
EmbeddedAssemblies::install_preload_hooks_for_alc ()
249265
{
250-
#if defined (NET6) && defined (NET6_ALC_WORKS)
251266
mono_install_assembly_preload_hook_v3 (
252267
open_from_bundles,
253268
nullptr /* user_data */,
254269
0 /* append */
255270
);
256-
#else // def NET6 && def NET6_ALC_WORKS
257-
mono_install_assembly_preload_hook (open_from_bundles_full, nullptr);
258-
#if !defined (NET6) // Reference-only loads don't exist in NET6
259-
mono_install_assembly_refonly_preload_hook (open_from_bundles_refonly, nullptr);
260-
#endif // !def NET6
261-
#endif // !(def NET6 && def NET6_ALC_WORKS)
262271
}
272+
#endif // def NET6
263273

264274
template<typename Key, typename Entry, int (*compare)(const Key*, const Entry*), bool use_extra_size>
265275
const Entry*

src/monodroid/jni/embedded-assemblies.hh

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <cstring>
66
#include <limits>
7+
#include <functional>
78
#include <mono/metadata/object.h>
89
#include <mono/metadata/assembly.h>
910

@@ -50,7 +51,10 @@ namespace xamarin::android::internal {
5051
#endif
5152
const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid);
5253

53-
void install_preload_hooks ();
54+
void install_preload_hooks_for_appdomains ();
55+
#if defined (NET6)
56+
void install_preload_hooks_for_alc ();
57+
#endif // def NET6
5458
MonoReflectionType* typemap_java_to_managed (MonoString *java_type);
5559

5660
/* returns current number of *all* assemblies found from all invocations */
@@ -93,11 +97,12 @@ namespace xamarin::android::internal {
9397
MonoReflectionType* typemap_java_to_managed (const char *java_type_name);
9498
size_t register_from (const char *apk_file, monodroid_should_register should_register);
9599
void gather_bundled_assemblies_from_apk (const char* apk, monodroid_should_register should_register);
96-
#if defined (NET6) && defined (NET6_ALC_WORKS)
100+
#if defined (NET6)
97101
MonoAssembly* open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, MonoError *error);
98-
#else // def NET6 && def NET6_ALC_WORKS
102+
#endif // def NET6
99103
MonoAssembly* open_from_bundles (MonoAssemblyName* aname, bool ref_only);
100-
#endif // !(def NET6 && def NET6_ALC_WORKS)
104+
MonoAssembly* open_from_bundles (MonoAssemblyName* aname, std::function<MonoImage*(char*, uint32_t, const char*)> loader, bool ref_only);
105+
101106
#if defined (DEBUG) || !defined (ANDROID)
102107
template<typename H>
103108
bool typemap_read_header (int dir_fd, const char *file_type, const char *dir_path, const char *file_path, uint32_t expected_magic, H &header, size_t &file_size, int &fd);
@@ -111,12 +116,12 @@ namespace xamarin::android::internal {
111116
bool register_debug_symbols_for_assembly (const char *entry_name, MonoBundledAssembly *assembly, const mono_byte *debug_contents, int debug_size);
112117

113118
static md_mmap_info md_mmap_apk_file (int fd, uint32_t offset, uint32_t size, const char* filename, const char* apk);
114-
#if defined (NET6) && defined (NET6_ALC_WORKS)
115-
static MonoAssembly* open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, char **assemblies_path, void *user_data, MonoError *error);
116-
#else // def NET6 && def NET6_ALC_WORKS
117119
static MonoAssembly* open_from_bundles_full (MonoAssemblyName *aname, char **assemblies_path, void *user_data);
120+
#if defined (NET6)
121+
static MonoAssembly* open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, char **assemblies_path, void *user_data, MonoError *error);
122+
#else // def NET6
118123
static MonoAssembly* open_from_bundles_refonly (MonoAssemblyName *aname, char **assemblies_path, void *user_data);
119-
#endif // !(def NET6 && def NET6_ALC_WORKS)
124+
#endif // ndef NET6
120125
static void get_assembly_data (const MonoBundledAssembly *e, char*& assembly_data, uint32_t& assembly_data_size);
121126

122127
void zip_load_entries (int fd, const char *apk_name, monodroid_should_register should_register);

src/monodroid/jni/monodroid-glue-internal.hh

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,11 @@ namespace xamarin::android::internal
6464
true
6565
>;
6666

67+
using load_assemblies_context_type = MonoAssemblyLoadContextGCHandle;
6768
static constexpr pinvoke_library_map::size_type LIBRARY_MAP_INITIAL_BUCKET_COUNT = 1;
68-
#endif // def NET6
69+
#else // def NET6
70+
using load_assemblies_context_type = MonoDomain*;
71+
#endif // ndef NET6
6972

7073
#if defined (DEBUG) && !defined (WINDOWS)
7174
struct RuntimeOptions {
@@ -107,6 +110,9 @@ namespace xamarin::android::internal
107110
};
108111

109112
private:
113+
static constexpr char MONO_ANDROID_ASSEMBLY_NAME[] = "Mono.Android";
114+
static constexpr char JAVA_INTEROP_ASSEMBLY_NAME[] = "Java.Interop";
115+
110116
static constexpr size_t SMALL_STRING_PARSE_BUFFER_LEN = 50;
111117
static constexpr bool is_running_on_desktop =
112118
#if ANDROID
@@ -222,7 +228,11 @@ namespace xamarin::android::internal
222228
void disable_external_signal_handlers ();
223229
void lookup_bridge_info (MonoDomain *domain, MonoImage *image, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info);
224230
void load_assembly (MonoDomain *domain, jstring_wrapper &assembly);
225-
void load_assemblies (MonoDomain *domain, bool preload, jstring_array_wrapper &assemblies);
231+
#if defined (NET6)
232+
void load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, jstring_wrapper &assembly);
233+
#endif // ndef NET6
234+
void load_assemblies (load_assemblies_context_type ctx, bool preload, jstring_array_wrapper &assemblies);
235+
226236
void set_debug_options ();
227237
void parse_gdb_options ();
228238
void mono_runtime_init (dynamic_local_string<PROPERTY_VALUE_BUFFER_LEN>& runtime_args);
@@ -302,6 +312,8 @@ namespace xamarin::android::internal
302312
int current_context_id = -1;
303313

304314
#if defined (NET6)
315+
MonoAssemblyLoadContextGCHandle default_alc = nullptr;
316+
305317
static std::mutex pinvoke_map_write_lock;
306318
static pinvoke_api_map xa_pinvoke_map;
307319
static pinvoke_library_map other_pinvoke_map;

0 commit comments

Comments
 (0)