Browse Source

assembly_load_hook fallback for registering GDMonoAssemblies.

Fixes #18029.

There are ways to load assemblies that the search hook has no way of intercepting,
and handling itself. Such as loading from a byte[] in C# code.

We now handle these cases with a fallback assembly_load_hook,
to avoid crashes when this is indeed done.
Pieter-Jan Briers 7 years ago
parent
commit
ce4451b45f
2 changed files with 45 additions and 4 deletions
  1. 43 4
      modules/mono/mono_gd/gd_mono_assembly.cpp
  2. 2 0
      modules/mono/mono_gd/gd_mono_assembly.h

+ 43 - 4
modules/mono/mono_gd/gd_mono_assembly.cpp

@@ -43,6 +43,17 @@
 bool GDMonoAssembly::no_search = false;
 Vector<String> GDMonoAssembly::search_dirs;
 
+void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, void *user_data) {
+	// If our search and preload hooks fail to load the assembly themselves, the mono runtime still might.
+	// Just do Assembly.LoadFrom("/Full/Path/On/Disk.dll");
+	// In this case, we wouldn't have the assembly known in GDMono, which causes crashes
+	// if any class inside the assembly is looked up by Godot.
+	// And causing a lookup like that is as easy as throwing an exception defined in it...
+	// No, we can't make the assembly load hooks smart enough because they get passed a MonoAssemblyName* only,
+	// not the disk path passed to say Assembly.LoadFrom().
+	_wrap_mono_assembly(assembly);
+}
+
 MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) {
 	return GDMonoAssembly::_search_hook(aname, user_data, false);
 }
@@ -137,16 +148,21 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
 		}
 	}
 
+	if (no_search)
+		return NULL;
+
+	no_search = true;
+
 	String name = mono_assembly_name_get_name(aname);
 	bool has_extension = name.ends_with(".dll");
 
+	GDMonoAssembly *res = NULL;
 	if (has_extension ? name == "mscorlib.dll" : name == "mscorlib") {
 		GDMonoAssembly **stored_assembly = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
 		if (stored_assembly)
 			return (*stored_assembly)->get_assembly();
 
 		String path;
-		GDMonoAssembly *res = NULL;
 
 		for (int i = 0; i < search_dirs.size(); i++) {
 			const String &search_dir = search_dirs[i];
@@ -167,11 +183,11 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
 				}
 			}
 		}
-
-		return res ? res->get_assembly() : NULL;
 	}
 
-	return NULL;
+	no_search = false;
+
+	return res ? res->get_assembly() : NULL;
 }
 
 GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) {
@@ -191,12 +207,35 @@ GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const
 	return assembly;
 }
 
+void GDMonoAssembly::_wrap_mono_assembly(MonoAssembly *assembly) {
+	String p_name = mono_assembly_name_get_name(mono_assembly_get_name(assembly));
+	GDMonoAssembly **existingassembly = GDMono::get_singleton()->get_loaded_assembly(p_name);
+
+	if (no_search || existingassembly != NULL) {
+		// Not sure whether the existingassembly check matters
+		// since no_search handles it in most cases?
+		// Can't hurt.
+		return;
+	}
+
+	MonoImage *image = mono_assembly_get_image(assembly);
+	mono_image_addref(image);
+
+	GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(p_name, mono_image_get_filename(image)));
+	gdassembly->assembly = assembly;
+	gdassembly->loaded = true;
+	gdassembly->image = image;
+	MonoDomain *domain = mono_domain_get();
+	GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, gdassembly);
+}
+
 void GDMonoAssembly::initialize() {
 
 	mono_install_assembly_search_hook(&assembly_search_hook, NULL);
 	mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, NULL);
 	mono_install_assembly_preload_hook(&assembly_preload_hook, NULL);
 	mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, NULL);
+	mono_install_assembly_load_hook(&assembly_load_hook, NULL);
 }
 
 Error GDMonoAssembly::load(bool p_refonly) {

+ 2 - 0
modules/mono/mono_gd/gd_mono_assembly.h

@@ -91,6 +91,7 @@ class GDMonoAssembly {
 	static bool no_search;
 	static Vector<String> search_dirs;
 
+	static void assembly_load_hook(MonoAssembly *assembly, void *user_data);
 	static MonoAssembly *assembly_search_hook(MonoAssemblyName *aname, void *user_data);
 	static MonoAssembly *assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data);
 	static MonoAssembly *assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
@@ -100,6 +101,7 @@ class GDMonoAssembly {
 	static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
 
 	static GDMonoAssembly *_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly);
+	static void _wrap_mono_assembly(MonoAssembly *assembly);
 
 	friend class GDMono;
 	static void initialize();