summaryrefslogtreecommitdiffstats
path: root/modules/mono/mono_gd/gd_mono_assembly.cpp
diff options
context:
space:
mode:
authorIgnacio Etcheverry <ignalfonsore@gmail.com>2020-05-09 20:54:07 +0200
committerIgnacio Etcheverry <ignalfonsore@gmail.com>2020-05-09 20:54:16 +0200
commitaf4acb5b1163089fe727000b39cefb4fc799f1db (patch)
tree5d46c1bc87ccfbe76a28569b0ff3ec652ce9ee61 /modules/mono/mono_gd/gd_mono_assembly.cpp
parent61306eb830cba932c57bc3e83ea462a49f2ebd97 (diff)
downloadredot-engine-af4acb5b1163089fe727000b39cefb4fc799f1db.tar.gz
C#/Mono: Check assembly version when loading
Not sure if we should check revision too, but this is good enough for what we want. This will be needed to load the correct Microsoft.Build when we switch to the nuget version.
Diffstat (limited to 'modules/mono/mono_gd/gd_mono_assembly.cpp')
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp71
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")