Skip to content

Commit

Permalink
[One .NET] Enable support for AssemblyLoadContext (#5940)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
grendello committed Jul 2, 2021
1 parent 4a7514c commit b7224e5
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 75 deletions.
100 changes: 55 additions & 45 deletions src/monodroid/jni/embedded-assemblies.cc
Expand Up @@ -130,21 +130,45 @@ EmbeddedAssemblies::get_assembly_data (const MonoBundledAssembly *e, char*& asse
}
}

#if defined (NET6)
MonoAssembly*
#if defined (NET6) && defined (NET6_ALC_WORKS)
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, MonoError *error)
#else // !(def NET6 && def NET6_ALC_WORKS)
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
#endif // def NET6 && def NET6_ALC_WORKS
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, [[maybe_unused]] MonoError *error)
{
#if defined (NET6)
// NET6 doesn't support reference-only loads, define the variable here to minimize ifdefs in the code below
#if defined (NET6_ALC_WORKS)
constexpr bool ref_only = false;
#else // def NET6_ALC_WORKS
ref_only = false;
#endif // ndef NET6_ALC_WORKS
auto loader = [&] (char *assembly_data, uint32_t assembly_data_size, const char *name) -> MonoImage* {
return mono_image_open_from_data_alc (
alc_gchandle,
assembly_data,
assembly_data_size,
0 /* need_copy */,
nullptr /* status */,
name
);
};

return open_from_bundles (aname, loader, false /* ref_only */);
}
#endif // def NET6

MonoAssembly*
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
{
auto loader = [&] (char *assembly_data, uint32_t assembly_data_size, const char *name) -> MonoImage* {
return mono_image_open_from_data_with_name (
assembly_data,
assembly_data_size,
0,
nullptr,
ref_only,
name
);
};

return open_from_bundles (aname, loader, ref_only);
}

MonoAssembly*
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, std::function<MonoImage*(char*, uint32_t, const char*)> loader, bool ref_only)
{
const char *culture = mono_assembly_name_get_culture (aname);
const char *asmname = mono_assembly_name_get_name (aname);

Expand Down Expand Up @@ -185,19 +209,7 @@ EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
}

get_assembly_data (e, assembly_data, assembly_data_size);

#if defined (NET6) && defined (NET6_ALC_WORKS)
image = mono_image_open_from_data_alc (
alc_gchandle,
assembly_data,
assembly_data_size,
0 /* need_copy */,
nullptr /* status */,
name.get ()
);
#else // (def NET6 && def NET6_ALC_WORKS)
image = mono_image_open_from_data_with_name (assembly_data, assembly_data_size, 0, nullptr, ref_only, name.get ());
#endif // !(def NET6 && def NET6_ALC_WORKS)
image = loader (assembly_data, assembly_data_size, name.get ());
if (image == nullptr) {
continue;
}
Expand All @@ -215,51 +227,49 @@ EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
log_info_nocheck (LOG_ASSEMBLY, "open_from_bundles: loaded assembly: %p\n", a);
}

#if defined (NET6) && defined (NET6_ALC_WORKS)
if (error != nullptr) {
error->error_code = a == nullptr ? MONO_ERROR_NONE : MONO_ERROR_FILE_NOT_FOUND;
}
#endif // def NET6 && def NET6_ALC_WORKS
return a;
}

#if defined (NET6) && defined (NET6_ALC_WORKS)
#if defined (NET6)
MonoAssembly*
EmbeddedAssemblies::open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data, MonoError *error)
{
log_warn (LOG_DEFAULT, __PRETTY_FUNCTION__);
return embeddedAssemblies.open_from_bundles (aname, alc_gchandle, error);
}
#else // def NET6 && def NET6_ALC_WORKS
#else // def NET6
MonoAssembly*
EmbeddedAssemblies::open_from_bundles_refonly (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data)
{
return embeddedAssemblies.open_from_bundles (aname, true);
}
#endif // ndef NET6

MonoAssembly*
EmbeddedAssemblies::open_from_bundles_full (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data)
{
return embeddedAssemblies.open_from_bundles (aname, false);
}

MonoAssembly*
EmbeddedAssemblies::open_from_bundles_refonly (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data)
void
EmbeddedAssemblies::install_preload_hooks_for_appdomains ()
{
return embeddedAssemblies.open_from_bundles (aname, true);
mono_install_assembly_preload_hook (open_from_bundles_full, nullptr);
#if !defined (NET6)
mono_install_assembly_refonly_preload_hook (open_from_bundles_refonly, nullptr);
#endif // ndef NET6
}
#endif // !(def NET6 && def NET6_ALC_WORKS)

#if defined (NET6)
void
EmbeddedAssemblies::install_preload_hooks ()
EmbeddedAssemblies::install_preload_hooks_for_alc ()
{
#if defined (NET6) && defined (NET6_ALC_WORKS)
mono_install_assembly_preload_hook_v3 (
open_from_bundles,
nullptr /* user_data */,
0 /* append */
);
#else // def NET6 && def NET6_ALC_WORKS
mono_install_assembly_preload_hook (open_from_bundles_full, nullptr);
#if !defined (NET6) // Reference-only loads don't exist in NET6
mono_install_assembly_refonly_preload_hook (open_from_bundles_refonly, nullptr);
#endif // !def NET6
#endif // !(def NET6 && def NET6_ALC_WORKS)
}
#endif // def NET6

template<typename Key, typename Entry, int (*compare)(const Key*, const Entry*), bool use_extra_size>
const Entry*
Expand Down
21 changes: 13 additions & 8 deletions src/monodroid/jni/embedded-assemblies.hh
Expand Up @@ -4,6 +4,7 @@

#include <cstring>
#include <limits>
#include <functional>
#include <mono/metadata/object.h>
#include <mono/metadata/assembly.h>

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

void install_preload_hooks ();
void install_preload_hooks_for_appdomains ();
#if defined (NET6)
void install_preload_hooks_for_alc ();
#endif // def NET6
MonoReflectionType* typemap_java_to_managed (MonoString *java_type);

/* returns current number of *all* assemblies found from all invocations */
Expand Down Expand Up @@ -93,11 +97,12 @@ namespace xamarin::android::internal {
MonoReflectionType* typemap_java_to_managed (const char *java_type_name);
size_t register_from (const char *apk_file, monodroid_should_register should_register);
void gather_bundled_assemblies_from_apk (const char* apk, monodroid_should_register should_register);
#if defined (NET6) && defined (NET6_ALC_WORKS)
#if defined (NET6)
MonoAssembly* open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, MonoError *error);
#else // def NET6 && def NET6_ALC_WORKS
#endif // def NET6
MonoAssembly* open_from_bundles (MonoAssemblyName* aname, bool ref_only);
#endif // !(def NET6 && def NET6_ALC_WORKS)
MonoAssembly* open_from_bundles (MonoAssemblyName* aname, std::function<MonoImage*(char*, uint32_t, const char*)> loader, bool ref_only);

#if defined (DEBUG) || !defined (ANDROID)
template<typename H>
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);
Expand All @@ -111,12 +116,12 @@ namespace xamarin::android::internal {
bool register_debug_symbols_for_assembly (const char *entry_name, MonoBundledAssembly *assembly, const mono_byte *debug_contents, int debug_size);

static md_mmap_info md_mmap_apk_file (int fd, uint32_t offset, uint32_t size, const char* filename, const char* apk);
#if defined (NET6) && defined (NET6_ALC_WORKS)
static MonoAssembly* open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, char **assemblies_path, void *user_data, MonoError *error);
#else // def NET6 && def NET6_ALC_WORKS
static MonoAssembly* open_from_bundles_full (MonoAssemblyName *aname, char **assemblies_path, void *user_data);
#if defined (NET6)
static MonoAssembly* open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, char **assemblies_path, void *user_data, MonoError *error);
#else // def NET6
static MonoAssembly* open_from_bundles_refonly (MonoAssemblyName *aname, char **assemblies_path, void *user_data);
#endif // !(def NET6 && def NET6_ALC_WORKS)
#endif // ndef NET6
static void get_assembly_data (const MonoBundledAssembly *e, char*& assembly_data, uint32_t& assembly_data_size);

void zip_load_entries (int fd, const char *apk_name, monodroid_should_register should_register);
Expand Down
16 changes: 14 additions & 2 deletions src/monodroid/jni/monodroid-glue-internal.hh
Expand Up @@ -64,8 +64,11 @@ namespace xamarin::android::internal
true
>;

using load_assemblies_context_type = MonoAssemblyLoadContextGCHandle;
static constexpr pinvoke_library_map::size_type LIBRARY_MAP_INITIAL_BUCKET_COUNT = 1;
#endif // def NET6
#else // def NET6
using load_assemblies_context_type = MonoDomain*;
#endif // ndef NET6

#if defined (DEBUG) && !defined (WINDOWS)
struct RuntimeOptions {
Expand Down Expand Up @@ -107,6 +110,9 @@ namespace xamarin::android::internal
};

private:
static constexpr char MONO_ANDROID_ASSEMBLY_NAME[] = "Mono.Android";
static constexpr char JAVA_INTEROP_ASSEMBLY_NAME[] = "Java.Interop";

static constexpr size_t SMALL_STRING_PARSE_BUFFER_LEN = 50;
static constexpr bool is_running_on_desktop =
#if ANDROID
Expand Down Expand Up @@ -222,7 +228,11 @@ namespace xamarin::android::internal
void disable_external_signal_handlers ();
void lookup_bridge_info (MonoDomain *domain, MonoImage *image, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info);
void load_assembly (MonoDomain *domain, jstring_wrapper &assembly);
void load_assemblies (MonoDomain *domain, bool preload, jstring_array_wrapper &assemblies);
#if defined (NET6)
void load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, jstring_wrapper &assembly);
#endif // ndef NET6
void load_assemblies (load_assemblies_context_type ctx, bool preload, jstring_array_wrapper &assemblies);

void set_debug_options ();
void parse_gdb_options ();
void mono_runtime_init (dynamic_local_string<PROPERTY_VALUE_BUFFER_LEN>& runtime_args);
Expand Down Expand Up @@ -302,6 +312,8 @@ namespace xamarin::android::internal
int current_context_id = -1;

#if defined (NET6)
MonoAssemblyLoadContextGCHandle default_alc = nullptr;

static std::mutex pinvoke_map_write_lock;
static pinvoke_api_map xa_pinvoke_map;
static pinvoke_library_map other_pinvoke_map;
Expand Down

0 comments on commit b7224e5

Please sign in to comment.