diff options
Diffstat (limited to 'modules/mono/mono_gd/gd_mono_assembly.cpp')
-rw-r--r-- | modules/mono/mono_gd/gd_mono_assembly.cpp | 71 |
1 files changed, 57 insertions, 14 deletions
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 0f211eebc6..da2ed2a7fc 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -33,6 +33,7 @@ #include <mono/metadata/mono-debug.h> #include <mono/metadata/tokentype.h> +#include "core/io/file_access_pack.h" #include "core/list.h" #include "core/os/file_access.h" #include "core/os/os.h" @@ -99,7 +100,7 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin // - The 'load' hook is called after the assembly has been loaded. Its job is to add the // assembly to the list of loaded assemblies so that the 'search' hook can look it up. -void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, void *user_data) { +void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, [[maybe_unused]] void *user_data) { String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly))); @@ -133,9 +134,7 @@ MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *an return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true); } -MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly) { - - (void)user_data; // UNUSED +MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, [[maybe_unused]] void *user_data, bool refonly) { String name = String::utf8(mono_assembly_name_get_name(aname)); bool has_extension = name.ends_with(".dll") || name.ends_with(".exe"); @@ -147,15 +146,13 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d return nullptr; } -MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, void *user_data, bool refonly) { - - (void)user_data; // UNUSED +MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, [[maybe_unused]] void *user_data, bool refonly) { String name = String::utf8(mono_assembly_name_get_name(aname)); - return _load_assembly_search(name, search_dirs, refonly); + return _load_assembly_search(name, aname, refonly, search_dirs); } -MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly) { +MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) { MonoAssembly *res = nullptr; String path; @@ -168,21 +165,21 @@ MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, const if (has_extension) { path = search_dir.plus_file(p_name); if (FileAccess::exists(path)) { - res = _real_load_assembly_from(path, p_refonly); + res = _real_load_assembly_from(path, p_refonly, p_aname); if (res != nullptr) return res; } } else { path = search_dir.plus_file(p_name + ".dll"); if (FileAccess::exists(path)) { - res = _real_load_assembly_from(path, p_refonly); + res = _real_load_assembly_from(path, p_refonly, p_aname); if (res != nullptr) return res; } path = search_dir.plus_file(p_name + ".exe"); if (FileAccess::exists(path)) { - res = _real_load_assembly_from(path, p_refonly); + res = _real_load_assembly_from(path, p_refonly, p_aname); if (res != nullptr) return res; } @@ -230,7 +227,7 @@ void GDMonoAssembly::initialize() { mono_install_assembly_load_hook(&assembly_load_hook, nullptr); } -MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly) { +MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname) { Vector<uint8_t> data = FileAccess::get_file_as_array(p_path); ERR_FAIL_COND_V_MSG(data.empty(), nullptr, "Could read the assembly in the specified location"); @@ -255,7 +252,33 @@ MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, boo true, &status, p_refonly, image_filename.utf8()); - ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, nullptr, "Failed to open assembly image from the loaded data"); + ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, nullptr, "Failed to open assembly image from memory: '" + p_path + "'."); + + if (p_aname != nullptr) { + // Check assembly version + const MonoTableInfo *table = mono_image_get_table_info(image, MONO_TABLE_ASSEMBLY); + + ERR_FAIL_NULL_V(table, nullptr); + + if (mono_table_info_get_rows(table)) { + uint32_t cols[MONO_ASSEMBLY_SIZE]; + mono_metadata_decode_row(table, 0, cols, MONO_ASSEMBLY_SIZE); + + // Not sure about .NET's policy. We will only ensure major and minor are equal, and ignore build and revision. + uint16_t major = cols[MONO_ASSEMBLY_MAJOR_VERSION]; + uint16_t minor = cols[MONO_ASSEMBLY_MINOR_VERSION]; + + uint16_t required_minor; + uint16_t required_major = mono_assembly_name_get_version(p_aname, &required_minor, nullptr, nullptr); + + if (required_major != 0) { + if (major != required_major && minor != required_minor) { + mono_image_close(image); + return nullptr; + } + } + } + } #ifdef DEBUG_ENABLED Vector<uint8_t> pdb_data; @@ -425,6 +448,26 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) return match; } +GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) { + + if (GDMono::get_singleton()->get_corlib_assembly() && (p_name == "mscorlib" || p_name == "mscorlib.dll")) + return GDMono::get_singleton()->get_corlib_assembly(); + + // We need to manually call the search hook in this case, as it won't be called in the next step + MonoAssembly *assembly = mono_assembly_invoke_search_hook(p_aname); + + if (!assembly) { + assembly = _load_assembly_search(p_name, p_aname, p_refonly, p_search_dirs); + ERR_FAIL_NULL_V(assembly, nullptr); + } + + GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); + ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?"); + ERR_FAIL_COND_V(loaded_asm->get_assembly() != assembly, nullptr); + + return loaded_asm; +} + GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) { if (p_name == "mscorlib" || p_name == "mscorlib.dll") |