From b7d1d80bf303268e87ed1cf35acfb170bb24da74 Mon Sep 17 00:00:00 2001 From: "Xamarin Public Jenkins (auto-signing)" Date: Wed, 28 Sep 2016 13:11:46 +0000 Subject: [PATCH] Imported Upstream version 4.6.1.3 Former-commit-id: a3e5816ec0030ec68ca623935b6704cc0369f223 --- configure.REMOVED.git-id | 2 +- configure.ac.REMOVED.git-id | 2 +- mcs/build/common/Consts.cs | 2 +- mcs/class/Makefile | 1 + .../Socket.cs.REMOVED.git-id | 2 +- .../SocketTest.cs.REMOVED.git-id | 2 +- .../monolite/Mono.Security.dll.REMOVED.git-id | 2 +- .../System.Configuration.dll.REMOVED.git-id | 2 +- .../System.Security.dll.REMOVED.git-id | 2 +- .../monolite/System.Xml.dll.REMOVED.git-id | 2 +- .../lib/monolite/System.dll.REMOVED.git-id | 2 +- .../lib/monolite/basic.exe.REMOVED.git-id | 2 +- .../lib/monolite/mscorlib.dll.REMOVED.git-id | 2 +- mcs/mcs/typespec.cs | 30 +- mono/metadata/appdomain.c | 93 +- mono/metadata/assembly.c | 3653 +++++++++++++++++ mono/metadata/assembly.c.REMOVED.git-id | 1 - mono/metadata/class.c.REMOVED.git-id | 2 +- mono/metadata/domain-internals.h | 4 - mono/metadata/domain.c | 10 - mono/mini/Makefile.am | 2 +- mono/mini/Makefile.am.in | 2 +- mono/mini/Makefile.in.REMOVED.git-id | 2 +- mono/mini/mini-arm64.c.REMOVED.git-id | 2 +- mono/mini/mini-llvm.c.REMOVED.git-id | 2 +- mono/mini/version.h | 2 +- mono/tests/Makefile.am | 20 +- mono/tests/Makefile.in | 21 +- mono/tests/reference-loader.cs | 111 - po/mcs/de.gmo | Bin 5406 -> 5406 bytes po/mcs/de.po.REMOVED.git-id | 2 +- po/mcs/es.gmo | Bin 16329 -> 16329 bytes po/mcs/es.po.REMOVED.git-id | 2 +- po/mcs/ja.gmo | Bin 20863 -> 20863 bytes po/mcs/ja.po.REMOVED.git-id | 2 +- po/mcs/mcs.pot | 4 +- po/mcs/pt_BR.gmo | Bin 73161 -> 73161 bytes po/mcs/pt_BR.po.REMOVED.git-id | 2 +- 38 files changed, 3749 insertions(+), 245 deletions(-) create mode 100644 mono/metadata/assembly.c delete mode 100644 mono/metadata/assembly.c.REMOVED.git-id delete mode 100644 mono/tests/reference-loader.cs diff --git a/configure.REMOVED.git-id b/configure.REMOVED.git-id index 0053420bfc..9138b9ab8a 100644 --- a/configure.REMOVED.git-id +++ b/configure.REMOVED.git-id @@ -1 +1 @@ -ac3c1fec80751ac6a5cc40288f081a109fea4933 \ No newline at end of file +cfd298f0214dbe147b607ec867f5a0700ae52f03 \ No newline at end of file diff --git a/configure.ac.REMOVED.git-id b/configure.ac.REMOVED.git-id index 13a209b835..c97ac83989 100644 --- a/configure.ac.REMOVED.git-id +++ b/configure.ac.REMOVED.git-id @@ -1 +1 @@ -7f99d462698fe5c7678472257969149675e4f373 \ No newline at end of file +f9f812ae034d2087f5a64846fa6446d74bc9f020 \ No newline at end of file diff --git a/mcs/build/common/Consts.cs b/mcs/build/common/Consts.cs index 7e216298a5..c38c17ef48 100644 --- a/mcs/build/common/Consts.cs +++ b/mcs/build/common/Consts.cs @@ -34,7 +34,7 @@ static class Consts // Use these assembly version constants to make code more maintainable. // - public const string MonoVersion = "4.6.0.0"; + public const string MonoVersion = "4.6.1.0"; public const string MonoCompany = "Mono development team"; public const string MonoProduct = "Mono Common Language Infrastructure"; public const string MonoCopyright = "(c) Various Mono authors"; diff --git a/mcs/class/Makefile b/mcs/class/Makefile index 06dbb9edf3..9ac4fb9618 100644 --- a/mcs/class/Makefile +++ b/mcs/class/Makefile @@ -107,6 +107,7 @@ xammac_4_5_dirs := \ System.ServiceModel.Internals \ SMDiagnostics \ System.Numerics \ + System.Numerics.Vectors \ Mono.Data.Tds \ System.Transactions \ System.EnterpriseServices \ diff --git a/mcs/class/System/System.Net.Sockets/Socket.cs.REMOVED.git-id b/mcs/class/System/System.Net.Sockets/Socket.cs.REMOVED.git-id index deae1e53fd..a17948af49 100644 --- a/mcs/class/System/System.Net.Sockets/Socket.cs.REMOVED.git-id +++ b/mcs/class/System/System.Net.Sockets/Socket.cs.REMOVED.git-id @@ -1 +1 @@ -3dd17d0c16ba42ad68b52821e4491c8eafe06c04 \ No newline at end of file +999ca900d5443337a86927d0d2ef2add4422f2b4 \ No newline at end of file diff --git a/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs.REMOVED.git-id b/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs.REMOVED.git-id index 8f4bbd4195..de77c9a66c 100644 --- a/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs.REMOVED.git-id +++ b/mcs/class/System/Test/System.Net.Sockets/SocketTest.cs.REMOVED.git-id @@ -1 +1 @@ -f4586a253f8ce09c2b2178d6f5e08ad53b55445a \ No newline at end of file +7d024238d081e85b535a28cb160e824d94975df7 \ No newline at end of file diff --git a/mcs/class/lib/monolite/Mono.Security.dll.REMOVED.git-id b/mcs/class/lib/monolite/Mono.Security.dll.REMOVED.git-id index f6a8567167..51606540a9 100644 --- a/mcs/class/lib/monolite/Mono.Security.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite/Mono.Security.dll.REMOVED.git-id @@ -1 +1 @@ -ad3993fd19839b18ea25f5027adfba1808a9b38e \ No newline at end of file +c083b93e7b6150b680d32a820f3b88c682ddf789 \ No newline at end of file diff --git a/mcs/class/lib/monolite/System.Configuration.dll.REMOVED.git-id b/mcs/class/lib/monolite/System.Configuration.dll.REMOVED.git-id index f30ce8de05..bb080995a3 100644 --- a/mcs/class/lib/monolite/System.Configuration.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite/System.Configuration.dll.REMOVED.git-id @@ -1 +1 @@ -1273821e71f05ca43fbda455ebe7d4c747213ee3 \ No newline at end of file +08ca578f5d7e96ee914dfbdbf084f73f3e8909b0 \ No newline at end of file diff --git a/mcs/class/lib/monolite/System.Security.dll.REMOVED.git-id b/mcs/class/lib/monolite/System.Security.dll.REMOVED.git-id index f1e970f4a6..bda85a8024 100644 --- a/mcs/class/lib/monolite/System.Security.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite/System.Security.dll.REMOVED.git-id @@ -1 +1 @@ -a03412e4144d551461b4f8b21d25f3258a2ec5f1 \ No newline at end of file +619913794ad60026c6830bab6dbfd0527c03dcb3 \ No newline at end of file diff --git a/mcs/class/lib/monolite/System.Xml.dll.REMOVED.git-id b/mcs/class/lib/monolite/System.Xml.dll.REMOVED.git-id index de771c777c..e97055478a 100644 --- a/mcs/class/lib/monolite/System.Xml.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite/System.Xml.dll.REMOVED.git-id @@ -1 +1 @@ -013e06f897446058c505edc676ba7cf78939b671 \ No newline at end of file +931b6d3637a95ae8cd8c492c6fa7126cffa90444 \ No newline at end of file diff --git a/mcs/class/lib/monolite/System.dll.REMOVED.git-id b/mcs/class/lib/monolite/System.dll.REMOVED.git-id index 3b134fe279..40dfca87bb 100644 --- a/mcs/class/lib/monolite/System.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite/System.dll.REMOVED.git-id @@ -1 +1 @@ -c3bbfb8e3d0bb1d4648a456800a8f616d8a28016 \ No newline at end of file +e1e9686ed7784f6a16ea693a569012bf15888449 \ No newline at end of file diff --git a/mcs/class/lib/monolite/basic.exe.REMOVED.git-id b/mcs/class/lib/monolite/basic.exe.REMOVED.git-id index 43e96dbad5..b5ce8df2be 100644 --- a/mcs/class/lib/monolite/basic.exe.REMOVED.git-id +++ b/mcs/class/lib/monolite/basic.exe.REMOVED.git-id @@ -1 +1 @@ -8410f10f87dea9524681b3f688dab2f70afbf9b5 \ No newline at end of file +9b7e5806ba59d76556574f901304d48e15f9520f \ No newline at end of file diff --git a/mcs/class/lib/monolite/mscorlib.dll.REMOVED.git-id b/mcs/class/lib/monolite/mscorlib.dll.REMOVED.git-id index 2ecd925178..b30f801159 100644 --- a/mcs/class/lib/monolite/mscorlib.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite/mscorlib.dll.REMOVED.git-id @@ -1 +1 @@ -d2b12594e92c61982d88980ec4bfaeb7c3bdf9ab \ No newline at end of file +48c23282983ab1e14ac9a68999ba56373af61c8d \ No newline at end of file diff --git a/mcs/mcs/typespec.cs b/mcs/mcs/typespec.cs index fb02370dbb..696bde556c 100644 --- a/mcs/mcs/typespec.cs +++ b/mcs/mcs/typespec.cs @@ -109,7 +109,6 @@ namespace Mono.CSharp var imported = MemberDefinition as ImportedTypeDefinition; if (imported != null && Kind != MemberKind.MissingType) imported.DefineInterfaces (this); - } return ifaces; @@ -1603,6 +1602,12 @@ namespace Mono.CSharp public TypeSpec Element { get; private set; } + public override IList Interfaces { + set { + throw new NotSupportedException (); + } + } + bool ITypeDefinition.IsComImport { get { return false; @@ -1777,13 +1782,19 @@ namespace Mono.CSharp readonly int rank; readonly ModuleContainer module; - private ArrayContainer (ModuleContainer module, TypeSpec element, int rank) + ArrayContainer (ModuleContainer module, TypeSpec element, int rank) : base (MemberKind.ArrayType, element, null) { this.module = module; this.rank = rank; } + public override IList Interfaces { + get { + return BaseType.Interfaces; + } + } + public int Rank { get { return rank; @@ -1926,7 +1937,6 @@ namespace Mono.CSharp if (!module.ArrayTypesCache.TryGetValue (key, out ac)) { ac = new ArrayContainer (module, element, rank); ac.BaseType = module.Compiler.BuiltinTypes.Array; - ac.Interfaces = ac.BaseType.Interfaces; module.ArrayTypesCache.Add (key, ac); } @@ -1942,11 +1952,17 @@ namespace Mono.CSharp class ReferenceContainer : ElementTypeSpec { - private ReferenceContainer (TypeSpec element) + ReferenceContainer (TypeSpec element) : base (MemberKind.Class, element, null) // TODO: Kind.Class is most likely wrong { } + public override IList Interfaces { + get { + return null; + } + } + public override MetaType GetMetaInfo () { if (info == null) { @@ -1977,6 +1993,12 @@ namespace Mono.CSharp state &= ~StateFlags.CLSCompliant_Undetected; } + public override IList Interfaces { + get { + return null; + } + } + public override MetaType GetMetaInfo () { if (info == null) { diff --git a/mono/metadata/appdomain.c b/mono/metadata/appdomain.c index 9b73ccd2bb..579b93afc1 100644 --- a/mono/metadata/appdomain.c +++ b/mono/metadata/appdomain.c @@ -1927,20 +1927,6 @@ mono_domain_assembly_search (MonoAssemblyName *aname, return NULL; } -static gboolean -prevent_running_reference_assembly (MonoAssembly *ass, MonoError *error) -{ - mono_error_init (error); - gboolean refasm = mono_assembly_get_reference_assembly_attribute (ass, error); - if (!is_ok (error)) - return TRUE; - if (refasm) { - mono_error_set_bad_image (error, ass->image, "Could not load file or assembly or one of its dependencies. Reference assemblies should not be loaded for execution. They can only be loaded in the Reflection-only loader context.\n"); - return TRUE; - } - return FALSE; -} - MonoReflectionAssembly * ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean refOnly) { @@ -1949,40 +1935,37 @@ ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean re MonoDomain *domain = mono_domain_get (); char *name, *filename; MonoImageOpenStatus status = MONO_IMAGE_OK; - MonoAssembly *ass = NULL; - - name = NULL; - result = NULL; - - mono_error_init (&error); + MonoAssembly *ass; if (fname == NULL) { - mono_error_set_argument_null (&error, "assemblyFile", ""); - goto leave; + MonoException *exc = mono_get_exception_argument_null ("assemblyFile"); + mono_set_pending_exception (exc); + return NULL; } name = filename = mono_string_to_utf8_checked (fname, &error); - if (!is_ok (&error)) - goto leave; + if (mono_error_set_pending_exception (&error)) + return NULL; ass = mono_assembly_open_full (filename, &status, refOnly); if (!ass) { + MonoException *exc; + if (status == MONO_IMAGE_IMAGE_INVALID) - mono_error_set_bad_image_name (&error, name, ""); + exc = mono_get_exception_bad_image_format2 (NULL, fname); else - mono_error_set_exception_instance (&error, mono_get_exception_file_not_found2 (NULL, fname)); - goto leave; + exc = mono_get_exception_file_not_found2 (NULL, fname); + g_free (name); + mono_set_pending_exception (exc); + return NULL; } - if (!refOnly && prevent_running_reference_assembly (ass, &error)) - goto leave; + g_free (name); result = mono_assembly_get_object_checked (domain, ass, &error); - -leave: - mono_error_set_pending_exception (&error); - g_free (name); + if (!result) + mono_error_set_pending_exception (&error); return result; } @@ -2017,11 +2000,6 @@ ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad, return NULL; } - if (!refonly && prevent_running_reference_assembly (ass, &error)) { - mono_error_set_pending_exception (&error); - return NULL; - } - refass = mono_assembly_get_object_checked (domain, ass, &error); if (!refass) mono_error_set_pending_exception (&error); @@ -2039,7 +2017,7 @@ ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad, MonoString *assRef, MonoAssembly *ass; MonoAssemblyName aname; MonoReflectionAssembly *refass = NULL; - gchar *name = NULL; + gchar *name; gboolean parsed; g_assert (assRef); @@ -2048,13 +2026,16 @@ ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad, MonoString *assRef, if (mono_error_set_pending_exception (&error)) return NULL; parsed = mono_assembly_name_parse (name, &aname); + g_free (name); if (!parsed) { /* This is a parse error... */ if (!refOnly) { refass = mono_try_assembly_resolve (domain, assRef, NULL, refOnly, &error); - if (!is_ok (&error)) - goto leave; + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } } return refass; } @@ -2066,31 +2047,25 @@ ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad, MonoString *assRef, /* MS.NET doesn't seem to call the assembly resolve handler for refonly assemblies */ if (!refOnly) { refass = mono_try_assembly_resolve (domain, assRef, NULL, refOnly, &error); - if (!is_ok (&error)) - goto leave; + if (!mono_error_ok (&error)) { + mono_error_set_pending_exception (&error); + return NULL; + } } else refass = NULL; - if (!refass) - goto leave; - ass = refass->assembly; + if (!refass) { + return NULL; + } } - if (!refOnly && prevent_running_reference_assembly (ass, &error)) - goto leave; - - g_assert (ass); - if (refass == NULL) { + if (refass == NULL) refass = mono_assembly_get_object_checked (domain, ass, &error); - if (!is_ok (&error)) - goto leave; - } - MONO_OBJECT_SETREF (refass, evidence, evidence); - -leave: - g_free (name); - mono_error_set_pending_exception (&error); + if (refass == NULL) + mono_error_set_pending_exception (&error); + else + MONO_OBJECT_SETREF (refass, evidence, evidence); return refass; } diff --git a/mono/metadata/assembly.c b/mono/metadata/assembly.c new file mode 100644 index 0000000000..57ce86ba71 --- /dev/null +++ b/mono/metadata/assembly.c @@ -0,0 +1,3653 @@ +/* + * assembly.c: Routines for loading assemblies. + * + * Author: + * Miguel de Icaza (miguel@ximian.com) + * + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011 Xamarin, Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#include +#include +#include +#include +#include +#include +#include "assembly.h" +#include "image.h" +#include "object-internals.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef HOST_WIN32 +#include +#include +#include +#endif + +#ifdef PLATFORM_MACOSX +#include +#endif + +/* AssemblyVersionMap: an assembly name, the assembly version set on which it is based, the assembly name it is replaced with and whether only versions lower than the current runtime version should be remapped */ +typedef struct { + const char* assembly_name; + guint8 version_set_index; + const char* new_assembly_name; + gboolean only_lower_versions; +} AssemblyVersionMap; + +/* the default search path is empty, the first slot is replaced with the computed value */ +static const char* +default_path [] = { + NULL, + NULL, + NULL +}; + +/* Contains the list of directories to be searched for assemblies (MONO_PATH) */ +static char **assemblies_path = NULL; + +/* Contains the list of directories that point to auxiliary GACs */ +static char **extra_gac_paths = NULL; + +#ifndef DISABLE_ASSEMBLY_REMAPPING +/* The list of system assemblies what will be remapped to the running + * runtime version. WARNING: this list must be sorted. + * The integer number is an index in the MonoRuntimeInfo structure, whose + * values can be found in domain.c - supported_runtimes. Look there + * to understand what remapping will be made. + * + * .NET version can be found at https://github.com/dotnet/coreclr/blob/master/src/inc/fxretarget.h#L99 + * + */ +static const AssemblyVersionMap framework_assemblies [] = { + {"Accessibility", 0}, + {"Commons.Xml.Relaxng", 0}, + {"I18N", 0}, + {"I18N.CJK", 0}, + {"I18N.MidEast", 0}, + {"I18N.Other", 0}, + {"I18N.Rare", 0}, + {"I18N.West", 0}, + {"Microsoft.Build.Engine", 2, NULL, TRUE}, + {"Microsoft.Build.Framework", 2, NULL, TRUE}, + {"Microsoft.Build.Tasks", 2, "Microsoft.Build.Tasks.v4.0"}, + {"Microsoft.Build.Tasks.v3.5", 2, "Microsoft.Build.Tasks.v4.0"}, + {"Microsoft.Build.Utilities", 2, "Microsoft.Build.Utilities.v4.0"}, + {"Microsoft.Build.Utilities.v3.5", 2, "Microsoft.Build.Utilities.v4.0"}, + {"Microsoft.VisualBasic", 1}, + {"Microsoft.VisualC", 1}, + {"Mono.Cairo", 0}, + {"Mono.CompilerServices.SymbolWriter", 0}, + {"Mono.Data", 0}, + {"Mono.Data.SybaseClient", 0}, + {"Mono.Data.Tds", 0}, + {"Mono.Data.TdsClient", 0}, + {"Mono.GetOptions", 0}, + {"Mono.Http", 0}, + {"Mono.Posix", 0}, + {"Mono.Security", 0}, + {"Mono.Security.Win32", 0}, + {"Mono.Xml.Ext", 0}, + {"Novell.Directory.Ldap", 0}, + {"PEAPI", 0}, + {"System", 0}, + {"System.ComponentModel.Composition", 2}, + {"System.ComponentModel.DataAnnotations", 2}, + {"System.Configuration", 0}, + {"System.Configuration.Install", 0}, + {"System.Core", 2}, + {"System.Data", 0}, + {"System.Data.Linq", 2}, + {"System.Data.OracleClient", 0}, + {"System.Data.Services", 2}, + {"System.Data.Services.Client", 2}, + {"System.Data.SqlXml", 0}, + {"System.Design", 0}, + {"System.DirectoryServices", 0}, + {"System.Drawing", 0}, + {"System.Drawing.Design", 0}, + {"System.EnterpriseServices", 0}, + {"System.IdentityModel", 3}, + {"System.IdentityModel.Selectors", 3}, + {"System.Management", 0}, + {"System.Messaging", 0}, + {"System.Net", 2}, + {"System.Runtime.Remoting", 0}, + {"System.Runtime.Serialization", 3}, + {"System.Runtime.Serialization.Formatters.Soap", 0}, + {"System.Security", 0}, + {"System.ServiceModel", 3}, + {"System.ServiceModel.Web", 2}, + {"System.ServiceProcess", 0}, + {"System.Transactions", 0}, + {"System.Web", 0}, + {"System.Web.Abstractions", 2}, + {"System.Web.DynamicData", 2}, + {"System.Web.Extensions", 2}, + {"System.Web.Mobile", 0}, + {"System.Web.Routing", 2}, + {"System.Web.Services", 0}, + {"System.Windows.Forms", 0}, + {"System.Xml", 0}, + {"System.Xml.Linq", 2}, + {"WindowsBase", 3}, + {"mscorlib", 0} +}; +#endif + +/* + * keeps track of loaded assemblies + */ +static GList *loaded_assemblies = NULL; +static MonoAssembly *corlib; + +#if defined(__native_client__) + +/* On Native Client, allow mscorlib to be loaded from memory */ +/* instead of loaded off disk. If these are not set, default */ +/* mscorlib loading will take place */ + +/* NOTE: If mscorlib data is passed to mono in this way then */ +/* it needs to remain allocated during the use of mono. */ + +static void *corlibData = NULL; +static size_t corlibSize = 0; + +void +mono_set_corlib_data (void *data, size_t size) +{ + corlibData = data; + corlibSize = size; +} + +#endif + +static char* unquote (const char *str); + +/* This protects loaded_assemblies and image->references */ +#define mono_assemblies_lock() mono_os_mutex_lock (&assemblies_mutex) +#define mono_assemblies_unlock() mono_os_mutex_unlock (&assemblies_mutex) +static mono_mutex_t assemblies_mutex; + +/* If defined, points to the bundled assembly information */ +const MonoBundledAssembly **bundles; + +static mono_mutex_t assembly_binding_mutex; + +/* Loaded assembly binding info */ +static GSList *loaded_assembly_bindings = NULL; + +/* Class lazy loading functions */ +static GENERATE_TRY_GET_CLASS_WITH_CACHE (internals_visible, System.Runtime.CompilerServices, InternalsVisibleToAttribute) + +static MonoAssembly* +mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly, gboolean postload); +static MonoAssembly* +mono_assembly_load_full_internal (MonoAssemblyName *aname, MonoAssembly *requesting, const char *basedir, MonoImageOpenStatus *status, gboolean refonly); +static MonoBoolean +mono_assembly_is_in_gac (const gchar *filanem); + +static gchar* +encode_public_tok (const guchar *token, gint32 len) +{ + const static gchar allowed [] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + gchar *res; + int i; + + res = (gchar *)g_malloc (len * 2 + 1); + for (i = 0; i < len; i++) { + res [i * 2] = allowed [token [i] >> 4]; + res [i * 2 + 1] = allowed [token [i] & 0xF]; + } + res [len * 2] = 0; + return res; +} + +/** + * mono_public_tokens_are_equal: + * @pubt1: first public key token + * @pubt2: second public key token + * + * Compare two public key tokens and return #TRUE is they are equal and #FALSE + * otherwise. + */ +gboolean +mono_public_tokens_are_equal (const unsigned char *pubt1, const unsigned char *pubt2) +{ + return memcmp (pubt1, pubt2, 16) == 0; +} + +/** + * mono_set_assemblies_path: + * @path: list of paths that contain directories where Mono will look for assemblies + * + * Use this method to override the standard assembly lookup system and + * override any assemblies coming from the GAC. This is the method + * that supports the MONO_PATH variable. + * + * Notice that MONO_PATH and this method are really a very bad idea as + * it prevents the GAC from working and it prevents the standard + * resolution mechanisms from working. Nonetheless, for some debugging + * situations and bootstrapping setups, this is useful to have. + */ +void +mono_set_assemblies_path (const char* path) +{ + char **splitted, **dest; + + splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000); + if (assemblies_path) + g_strfreev (assemblies_path); + assemblies_path = dest = splitted; + while (*splitted) { + char *tmp = *splitted; + if (*tmp) + *dest++ = mono_path_canonicalize (tmp); + g_free (tmp); + splitted++; + } + *dest = *splitted; + + if (g_getenv ("MONO_DEBUG") == NULL) + return; + + splitted = assemblies_path; + while (*splitted) { + if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR)) + g_warning ("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *splitted); + + splitted++; + } +} + +/* Native Client can't get this info from an environment variable so */ +/* it's passed in to the runtime, or set manually by embedding code. */ +#ifdef __native_client__ +char* nacl_mono_path = NULL; +#endif + +static void +check_path_env (void) +{ + const char* path; + path = g_getenv ("MONO_PATH"); +#ifdef __native_client__ + if (!path) + path = nacl_mono_path; +#endif + if (!path || assemblies_path != NULL) + return; + + mono_set_assemblies_path(path); +} + +static void +check_extra_gac_path_env (void) { + const char *path; + char **splitted, **dest; + + path = g_getenv ("MONO_GAC_PREFIX"); + if (!path) + return; + + splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000); + if (extra_gac_paths) + g_strfreev (extra_gac_paths); + extra_gac_paths = dest = splitted; + while (*splitted){ + if (**splitted) + *dest++ = *splitted; + splitted++; + } + *dest = *splitted; + + if (g_getenv ("MONO_DEBUG") == NULL) + return; + + while (*splitted) { + if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR)) + g_warning ("'%s' in MONO_GAC_PREFIX doesn't exist or has wrong permissions.", *splitted); + + splitted++; + } +} + +static gboolean +assembly_binding_maps_name (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname) +{ + if (!info || !info->name) + return FALSE; + + if (strcmp (info->name, aname->name)) + return FALSE; + + if (info->major != aname->major || info->minor != aname->minor) + return FALSE; + + if ((info->culture != NULL && info->culture [0]) != (aname->culture != NULL && aname->culture [0])) + return FALSE; + + if (info->culture && aname->culture && strcmp (info->culture, aname->culture)) + return FALSE; + + if (!mono_public_tokens_are_equal (info->public_key_token, aname->public_key_token)) + return FALSE; + + return TRUE; +} + +static void +mono_assembly_binding_info_free (MonoAssemblyBindingInfo *info) +{ + if (!info) + return; + + g_free (info->name); + g_free (info->culture); +} + +static void +get_publisher_policy_info (MonoImage *image, MonoAssemblyName *aname, MonoAssemblyBindingInfo *binding_info) +{ + MonoTableInfo *t; + guint32 cols [MONO_MANIFEST_SIZE]; + const gchar *filename; + gchar *subpath, *fullpath; + + t = &image->tables [MONO_TABLE_MANIFESTRESOURCE]; + /* MS Impl. accepts policy assemblies with more than + * one manifest resource, and only takes the first one */ + if (t->rows < 1) { + binding_info->is_valid = FALSE; + return; + } + + mono_metadata_decode_row (t, 0, cols, MONO_MANIFEST_SIZE); + if ((cols [MONO_MANIFEST_IMPLEMENTATION] & MONO_IMPLEMENTATION_MASK) != MONO_IMPLEMENTATION_FILE) { + binding_info->is_valid = FALSE; + return; + } + + filename = mono_metadata_string_heap (image, cols [MONO_MANIFEST_NAME]); + g_assert (filename != NULL); + + subpath = g_path_get_dirname (image->name); + fullpath = g_build_path (G_DIR_SEPARATOR_S, subpath, filename, NULL); + mono_config_parse_publisher_policy (fullpath, binding_info); + g_free (subpath); + g_free (fullpath); + + /* Define the optional elements/attributes before checking */ + if (!binding_info->culture) + binding_info->culture = g_strdup (""); + + /* Check that the most important elements/attributes exist */ + if (!binding_info->name || !binding_info->public_key_token [0] || !binding_info->has_old_version_bottom || + !binding_info->has_new_version || !assembly_binding_maps_name (binding_info, aname)) { + mono_assembly_binding_info_free (binding_info); + binding_info->is_valid = FALSE; + return; + } + + binding_info->is_valid = TRUE; +} + +static int +compare_versions (AssemblyVersionSet *v, MonoAssemblyName *aname) +{ + if (v->major > aname->major) + return 1; + else if (v->major < aname->major) + return -1; + + if (v->minor > aname->minor) + return 1; + else if (v->minor < aname->minor) + return -1; + + if (v->build > aname->build) + return 1; + else if (v->build < aname->build) + return -1; + + if (v->revision > aname->revision) + return 1; + else if (v->revision < aname->revision) + return -1; + + return 0; +} + +static gboolean +check_policy_versions (MonoAssemblyBindingInfo *info, MonoAssemblyName *name) +{ + if (!info->is_valid) + return FALSE; + + /* If has_old_version_top doesn't exist, we don't have an interval */ + if (!info->has_old_version_top) { + if (compare_versions (&info->old_version_bottom, name) == 0) + return TRUE; + + return FALSE; + } + + /* Check that the version defined by name is valid for the interval */ + if (compare_versions (&info->old_version_top, name) < 0) + return FALSE; + + /* We should be greater or equal than the small version */ + if (compare_versions (&info->old_version_bottom, name) > 0) + return FALSE; + + return TRUE; +} + +/** + * mono_assembly_names_equal: + * @l: first assembly + * @r: second assembly. + * + * Compares two MonoAssemblyNames and returns whether they are equal. + * + * This compares the names, the cultures, the release version and their + * public tokens. + * + * Returns: TRUE if both assembly names are equal. + */ +gboolean +mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r) +{ + if (!l->name || !r->name) + return FALSE; + + if (strcmp (l->name, r->name)) + return FALSE; + + if (l->culture && r->culture && strcmp (l->culture, r->culture)) + return FALSE; + + if (l->major != r->major || l->minor != r->minor || + l->build != r->build || l->revision != r->revision) + if (! ((l->major == 0 && l->minor == 0 && l->build == 0 && l->revision == 0) || (r->major == 0 && r->minor == 0 && r->build == 0 && r->revision == 0))) + return FALSE; + + if (!l->public_key_token [0] || !r->public_key_token [0]) + return TRUE; + + if (!mono_public_tokens_are_equal (l->public_key_token, r->public_key_token)) + return FALSE; + + return TRUE; +} + +static MonoAssembly * +load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status, MonoBoolean refonly) +{ + int i; + char *fullpath; + MonoAssembly *result; + + for (i = 0; search_path [i]; ++i) { + fullpath = g_build_filename (search_path [i], basename, NULL); + result = mono_assembly_open_full (fullpath, status, refonly); + g_free (fullpath); + if (result) + return result; + } + return NULL; +} + +/** + * mono_assembly_setrootdir: + * @root_dir: The pathname of the root directory where we will locate assemblies + * + * This routine sets the internal default root directory for looking up + * assemblies. + * + * This is used by Windows installations to compute dynamically the + * place where the Mono assemblies are located. + * + */ +void +mono_assembly_setrootdir (const char *root_dir) +{ + /* + * Override the MONO_ASSEMBLIES directory configured at compile time. + */ + /* Leak if called more than once */ + default_path [0] = g_strdup (root_dir); +} + +/** + * mono_assembly_getrootdir: + * + * Obtains the root directory used for looking up assemblies. + * + * Returns: a string with the directory, this string should not be freed. + */ +G_CONST_RETURN gchar * +mono_assembly_getrootdir (void) +{ + return default_path [0]; +} + +/** + * mono_set_dirs: + * @assembly_dir: the base directory for assemblies + * @config_dir: the base directory for configuration files + * + * This routine is used internally and by developers embedding + * the runtime into their own applications. + * + * There are a number of cases to consider: Mono as a system-installed + * package that is available on the location preconfigured or Mono in + * a relocated location. + * + * If you are using a system-installed Mono, you can pass NULL + * to both parameters. If you are not, you should compute both + * directory values and call this routine. + * + * The values for a given PREFIX are: + * + * assembly_dir: PREFIX/lib + * config_dir: PREFIX/etc + * + * Notice that embedders that use Mono in a relocated way must + * compute the location at runtime, as they will be in control + * of where Mono is installed. + */ +void +mono_set_dirs (const char *assembly_dir, const char *config_dir) +{ + if (assembly_dir == NULL) + assembly_dir = mono_config_get_assemblies_dir (); + if (config_dir == NULL) + config_dir = mono_config_get_cfg_dir (); + mono_assembly_setrootdir (assembly_dir); + mono_set_config_dir (config_dir); +} + +#ifndef HOST_WIN32 + +static char * +compute_base (char *path) +{ + char *p = strrchr (path, '/'); + if (p == NULL) + return NULL; + + /* Not a well known Mono executable, we are embedded, cant guess the base */ + if (strcmp (p, "/mono") && strcmp (p, "/mono-boehm") && strcmp (p, "/mono-sgen") && strcmp (p, "/pedump") && strcmp (p, "/monodis")) + return NULL; + + *p = 0; + p = strrchr (path, '/'); + if (p == NULL) + return NULL; + + if (strcmp (p, "/bin") != 0) + return NULL; + *p = 0; + return path; +} + +static void +fallback (void) +{ + mono_set_dirs (mono_config_get_assemblies_dir (), mono_config_get_cfg_dir ()); +} + +static G_GNUC_UNUSED void +set_dirs (char *exe) +{ + char *base; + char *config, *lib, *mono; + struct stat buf; + const char *bindir; + + /* + * Only /usr prefix is treated specially + */ + bindir = mono_config_get_bin_dir (); + g_assert (bindir); + if (strncmp (exe, bindir, strlen (bindir)) == 0 || (base = compute_base (exe)) == NULL){ + fallback (); + return; + } + + config = g_build_filename (base, "etc", NULL); + lib = g_build_filename (base, "lib", NULL); + mono = g_build_filename (lib, "mono/4.5", NULL); // FIXME: stop hardcoding 4.5 here + if (stat (mono, &buf) == -1) + fallback (); + else { + mono_set_dirs (lib, config); + } + + g_free (config); + g_free (lib); + g_free (mono); +} + +#endif /* HOST_WIN32 */ + +/** + * mono_set_rootdir: + * + * Registers the root directory for the Mono runtime, for Linux and Solaris 10, + * this auto-detects the prefix where Mono was installed. + */ +void +mono_set_rootdir (void) +{ +#if defined(HOST_WIN32) || (defined(PLATFORM_MACOSX) && !defined(TARGET_ARM)) + gchar *bindir, *installdir, *root, *name, *resolvedname, *config; + +#ifdef HOST_WIN32 + name = mono_get_module_file_name ((HMODULE) &__ImageBase); +#else + { + /* + * _NSGetExecutablePath may return -1 to indicate buf is not large + * enough, but we ignore that case to avoid having to do extra dynamic + * allocation for the path and hope that 4096 is enough - this is + * ok in the Linux/Solaris case below at least... + */ + + gchar buf[4096]; + guint buf_size = sizeof (buf); + + name = NULL; + if (_NSGetExecutablePath (buf, &buf_size) == 0) + name = g_strdup (buf); + + if (name == NULL) { + fallback (); + return; + } + } +#endif + + resolvedname = mono_path_resolve_symlinks (name); + + bindir = g_path_get_dirname (resolvedname); + installdir = g_path_get_dirname (bindir); + root = g_build_path (G_DIR_SEPARATOR_S, installdir, "lib", NULL); + + config = g_build_filename (root, "..", "etc", NULL); +#ifdef HOST_WIN32 + mono_set_dirs (root, config); +#else + if (g_file_test (root, G_FILE_TEST_EXISTS) && g_file_test (config, G_FILE_TEST_EXISTS)) + mono_set_dirs (root, config); + else + fallback (); +#endif + + g_free (config); + g_free (root); + g_free (installdir); + g_free (bindir); + g_free (name); + g_free (resolvedname); +#elif defined(DISABLE_MONO_AUTODETECTION) + fallback (); +#else + char buf [4096]; + int s; + char *str; + + /* Linux style */ + s = readlink ("/proc/self/exe", buf, sizeof (buf)-1); + + if (s != -1){ + buf [s] = 0; + set_dirs (buf); + return; + } + + /* Solaris 10 style */ + str = g_strdup_printf ("/proc/%d/path/a.out", getpid ()); + s = readlink (str, buf, sizeof (buf)-1); + g_free (str); + if (s != -1){ + buf [s] = 0; + set_dirs (buf); + return; + } + fallback (); +#endif +} + +/** + * mono_assemblies_init: + * + * Initialize global variables used by this module. + */ +void +mono_assemblies_init (void) +{ + /* + * Initialize our internal paths if we have not been initialized yet. + * This happens when embedders use Mono. + */ + if (mono_assembly_getrootdir () == NULL) + mono_set_rootdir (); + + check_path_env (); + check_extra_gac_path_env (); + + mono_os_mutex_init_recursive (&assemblies_mutex); + mono_os_mutex_init (&assembly_binding_mutex); +} + +static void +mono_assembly_binding_lock (void) +{ + mono_locks_os_acquire (&assembly_binding_mutex, AssemblyBindingLock); +} + +static void +mono_assembly_binding_unlock (void) +{ + mono_locks_os_release (&assembly_binding_mutex, AssemblyBindingLock); +} + +gboolean +mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname) +{ + MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY]; + guint32 cols [MONO_ASSEMBLY_SIZE]; + gint32 machine, flags; + + if (!t->rows) + return FALSE; + + mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE); + + aname->hash_len = 0; + aname->hash_value = NULL; + aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]); + aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]); + aname->flags = cols [MONO_ASSEMBLY_FLAGS]; + aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION]; + aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION]; + aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER]; + aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER]; + aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG]; + if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) { + guchar* token = (guchar *)g_malloc (8); + gchar* encoded; + const gchar* pkey; + int len; + + pkey = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]); + len = mono_metadata_decode_blob_size (pkey, &pkey); + aname->public_key = (guchar*)pkey; + + mono_digest_get_public_token (token, aname->public_key, len); + encoded = encode_public_tok (token, 8); + g_strlcpy ((char*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH); + + g_free (encoded); + g_free (token); + } + else { + aname->public_key = NULL; + memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH); + } + + if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) { + aname->public_key = (guchar*)mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]); + } + else + aname->public_key = 0; + + machine = ((MonoCLIImageInfo*)(image->image_info))->cli_header.coff.coff_machine; + flags = ((MonoCLIImageInfo*)(image->image_info))->cli_cli_header.ch_flags; + switch (machine) { + case COFF_MACHINE_I386: + /* https://bugzilla.xamarin.com/show_bug.cgi?id=17632 */ + if (flags & (CLI_FLAGS_32BITREQUIRED|CLI_FLAGS_PREFERRED32BIT)) + aname->arch = MONO_PROCESSOR_ARCHITECTURE_X86; + else if ((flags & 0x70) == 0x70) + aname->arch = MONO_PROCESSOR_ARCHITECTURE_NONE; + else + aname->arch = MONO_PROCESSOR_ARCHITECTURE_MSIL; + break; + case COFF_MACHINE_IA64: + aname->arch = MONO_PROCESSOR_ARCHITECTURE_IA64; + break; + case COFF_MACHINE_AMD64: + aname->arch = MONO_PROCESSOR_ARCHITECTURE_AMD64; + break; + case COFF_MACHINE_ARM: + aname->arch = MONO_PROCESSOR_ARCHITECTURE_ARM; + break; + default: + break; + } + + return TRUE; +} + +/** + * mono_stringify_assembly_name: + * @aname: the assembly name. + * + * Convert @aname into its string format. The returned string is dynamically + * allocated and should be freed by the caller. + * + * Returns: a newly allocated string with a string representation of + * the assembly name. + */ +char* +mono_stringify_assembly_name (MonoAssemblyName *aname) +{ + const char *quote = (aname->name && g_ascii_isspace (aname->name [0])) ? "\"" : ""; + + return g_strdup_printf ( + "%s%s%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s%s", + quote, aname->name, quote, + aname->major, aname->minor, aname->build, aname->revision, + aname->culture && *aname->culture? aname->culture: "neutral", + aname->public_key_token [0] ? (char *)aname->public_key_token : "null", + (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) ? ", Retargetable=Yes" : ""); +} + +static gchar* +assemblyref_public_tok (MonoImage *image, guint32 key_index, guint32 flags) +{ + const gchar *public_tok; + int len; + + public_tok = mono_metadata_blob_heap (image, key_index); + len = mono_metadata_decode_blob_size (public_tok, &public_tok); + + if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) { + guchar token [8]; + mono_digest_get_public_token (token, (guchar*)public_tok, len); + return encode_public_tok (token, 8); + } + + return encode_public_tok ((guchar*)public_tok, len); +} + +/** + * mono_assembly_addref: + * @assemnly: the assembly to reference + * + * This routine increments the reference count on a MonoAssembly. + * The reference count is reduced every time the method mono_assembly_close() is + * invoked. + */ +void +mono_assembly_addref (MonoAssembly *assembly) +{ + InterlockedIncrement (&assembly->ref_count); +} + +/* + * CAUTION: This table must be kept in sync with + * ivkm/reflect/Fusion.cs + */ + +#define SILVERLIGHT_KEY "7cec85d7bea7798e" +#define WINFX_KEY "31bf3856ad364e35" +#define ECMA_KEY "b77a5c561934e089" +#define MSFINAL_KEY "b03f5f7f11d50a3a" + +typedef struct { + const char *name; + const char *from; + const char *to; +} KeyRemapEntry; + +static KeyRemapEntry key_remap_table[] = { + { "Microsoft.CSharp", WINFX_KEY, MSFINAL_KEY }, + { "System", SILVERLIGHT_KEY, ECMA_KEY }, + { "System.ComponentModel.Composition", WINFX_KEY, ECMA_KEY }, + { "System.ComponentModel.DataAnnotations", "ddd0da4d3e678217", WINFX_KEY }, + { "System.Core", SILVERLIGHT_KEY, ECMA_KEY }, + // FIXME: MS uses MSFINAL_KEY for .NET 4.5 + { "System.Net", SILVERLIGHT_KEY, MSFINAL_KEY }, + { "System.Numerics", WINFX_KEY, ECMA_KEY }, + { "System.Runtime.Serialization", SILVERLIGHT_KEY, ECMA_KEY }, + { "System.ServiceModel", WINFX_KEY, ECMA_KEY }, + { "System.ServiceModel.Web", SILVERLIGHT_KEY, WINFX_KEY }, + { "System.Windows", SILVERLIGHT_KEY, MSFINAL_KEY }, + { "System.Xml", SILVERLIGHT_KEY, ECMA_KEY }, + { "System.Xml.Linq", WINFX_KEY, ECMA_KEY }, + { "System.Xml.Serialization", WINFX_KEY, ECMA_KEY } +}; + +static void +remap_keys (MonoAssemblyName *aname) +{ + int i; + for (i = 0; i < G_N_ELEMENTS (key_remap_table); i++) { + const KeyRemapEntry *entry = &key_remap_table [i]; + + if (strcmp (aname->name, entry->name) || + !mono_public_tokens_are_equal (aname->public_key_token, (const unsigned char*) entry->from)) + continue; + + memcpy (aname->public_key_token, entry->to, MONO_PUBLIC_KEY_TOKEN_LENGTH); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "Remapped public key token of retargetable assembly %s from %s to %s", + aname->name, entry->from, entry->to); + return; + } +} + +static MonoAssemblyName * +mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_aname) +{ + const MonoRuntimeInfo *current_runtime; + int pos, first, last; + + if (aname->name == NULL) return aname; + + current_runtime = mono_get_runtime_info (); + + if (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) { + const AssemblyVersionSet* vset; + + /* Remap to current runtime */ + vset = ¤t_runtime->version_sets [0]; + + memcpy (dest_aname, aname, sizeof(MonoAssemblyName)); + dest_aname->major = vset->major; + dest_aname->minor = vset->minor; + dest_aname->build = vset->build; + dest_aname->revision = vset->revision; + dest_aname->flags &= ~ASSEMBLYREF_RETARGETABLE_FLAG; + + /* Remap assembly name */ + if (!strcmp (aname->name, "System.Net")) + dest_aname->name = g_strdup ("System"); + + remap_keys (dest_aname); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "The request to load the retargetable assembly %s v%d.%d.%d.%d was remapped to %s v%d.%d.%d.%d", + aname->name, + aname->major, aname->minor, aname->build, aname->revision, + dest_aname->name, + vset->major, vset->minor, vset->build, vset->revision + ); + + return dest_aname; + } + +#ifndef DISABLE_ASSEMBLY_REMAPPING + first = 0; + last = G_N_ELEMENTS (framework_assemblies) - 1; + + while (first <= last) { + int res; + pos = first + (last - first) / 2; + res = strcmp (aname->name, framework_assemblies[pos].assembly_name); + if (res == 0) { + const AssemblyVersionSet* vset; + int index = framework_assemblies[pos].version_set_index; + g_assert (index < G_N_ELEMENTS (current_runtime->version_sets)); + vset = ¤t_runtime->version_sets [index]; + + if (aname->major == vset->major && aname->minor == vset->minor && + aname->build == vset->build && aname->revision == vset->revision) + return aname; + + if (framework_assemblies[pos].only_lower_versions && compare_versions ((AssemblyVersionSet*)vset, aname) < 0) + return aname; + + if ((aname->major | aname->minor | aname->build | aname->revision) != 0) + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, + "The request to load the assembly %s v%d.%d.%d.%d was remapped to v%d.%d.%d.%d", + aname->name, + aname->major, aname->minor, aname->build, aname->revision, + vset->major, vset->minor, vset->build, vset->revision + ); + + memcpy (dest_aname, aname, sizeof(MonoAssemblyName)); + dest_aname->major = vset->major; + dest_aname->minor = vset->minor; + dest_aname->build = vset->build; + dest_aname->revision = vset->revision; + if (framework_assemblies[pos].new_assembly_name != NULL) { + dest_aname->name = framework_assemblies[pos].new_assembly_name; + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, + "The assembly name %s was remapped to %s", + aname->name, + dest_aname->name); + } + return dest_aname; + } else if (res < 0) { + last = pos - 1; + } else { + first = pos + 1; + } + } +#endif + + return aname; +} + +/** + * mono_assembly_get_assemblyref: + * @image: pointer to the MonoImage to extract the information from. + * @index: index to the assembly reference in the image. + * @aname: pointer to a `MonoAssemblyName` that will hold the returned value. + * + * Fills out the @aname with the assembly name of the @index assembly reference in @image. + */ +void +mono_assembly_get_assemblyref (MonoImage *image, int index, MonoAssemblyName *aname) +{ + MonoTableInfo *t; + guint32 cols [MONO_ASSEMBLYREF_SIZE]; + const char *hash; + + t = &image->tables [MONO_TABLE_ASSEMBLYREF]; + + mono_metadata_decode_row (t, index, cols, MONO_ASSEMBLYREF_SIZE); + + hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]); + aname->hash_len = mono_metadata_decode_blob_size (hash, &hash); + aname->hash_value = hash; + aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]); + aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]); + aname->flags = cols [MONO_ASSEMBLYREF_FLAGS]; + aname->major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION]; + aname->minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION]; + aname->build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER]; + aname->revision = cols [MONO_ASSEMBLYREF_REV_NUMBER]; + + if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) { + gchar *token = assemblyref_public_tok (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname->flags); + g_strlcpy ((char*)aname->public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH); + g_free (token); + } else { + memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH); + } +} + +void +mono_assembly_load_reference (MonoImage *image, int index) +{ + MonoAssembly *reference; + MonoAssemblyName aname; + MonoImageOpenStatus status; + + /* + * image->references is shared between threads, so we need to access + * it inside a critical section. + */ + mono_assemblies_lock (); + if (!image->references) { + MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLYREF]; + + image->references = g_new0 (MonoAssembly *, t->rows + 1); + image->nreferences = t->rows; + } + reference = image->references [index]; + mono_assemblies_unlock (); + if (reference) + return; + + mono_assembly_get_assemblyref (image, index, &aname); + + if (image->assembly && image->assembly->ref_only) { + /* We use the loaded corlib */ + if (!strcmp (aname.name, "mscorlib")) + reference = mono_assembly_load_full_internal (&aname, image->assembly, image->assembly->basedir, &status, FALSE); + else { + reference = mono_assembly_loaded_full (&aname, TRUE); + if (!reference) + /* Try a postload search hook */ + reference = mono_assembly_invoke_search_hook_internal (&aname, image->assembly, TRUE, TRUE); + } + + /* + * Here we must advice that the error was due to + * a non loaded reference using the ReflectionOnly api + */ + if (!reference) + reference = (MonoAssembly *)REFERENCE_MISSING; + } else { + /* we first try without setting the basedir: this can eventually result in a ResolveAssembly + * event which is the MS .net compatible behaviour (the assemblyresolve_event3.cs test has been fixed + * accordingly, it would fail on the MS runtime before). + * The second load attempt has the basedir set to keep compatibility with the old mono behavior, for + * example bug-349190.2.cs and who knows how much more code in the wild. + */ + reference = mono_assembly_load_full_internal (&aname, image->assembly, NULL, &status, FALSE); + if (!reference && image->assembly) + reference = mono_assembly_load_full_internal (&aname, image->assembly, image->assembly->basedir, &status, FALSE); + } + + if (reference == NULL){ + char *extra_msg; + + if (status == MONO_IMAGE_ERROR_ERRNO && errno == ENOENT) { + extra_msg = g_strdup_printf ("The assembly was not found in the Global Assembly Cache, a path listed in the MONO_PATH environment variable, or in the location of the executing assembly (%s).\n", image->assembly != NULL ? image->assembly->basedir : "" ); + } else if (status == MONO_IMAGE_ERROR_ERRNO) { + extra_msg = g_strdup_printf ("System error: %s\n", strerror (errno)); + } else if (status == MONO_IMAGE_MISSING_ASSEMBLYREF) { + extra_msg = g_strdup ("Cannot find an assembly referenced from this one.\n"); + } else if (status == MONO_IMAGE_IMAGE_INVALID) { + extra_msg = g_strdup ("The file exists but is not a valid assembly.\n"); + } else { + extra_msg = g_strdup (""); + } + + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, "The following assembly referenced from %s could not be loaded:\n" + " Assembly: %s (assemblyref_index=%d)\n" + " Version: %d.%d.%d.%d\n" + " Public Key: %s\n%s", + image->name, aname.name, index, + aname.major, aname.minor, aname.build, aname.revision, + strlen ((char*)aname.public_key_token) == 0 ? "(none)" : (char*)aname.public_key_token, extra_msg); + g_free (extra_msg); + } + + mono_assemblies_lock (); + if (reference == NULL) { + /* Flag as not found */ + reference = (MonoAssembly *)REFERENCE_MISSING; + } + + if (!image->references [index]) { + if (reference != REFERENCE_MISSING){ + mono_assembly_addref (reference); + if (image->assembly) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Ref addref %s[%p] -> %s[%p]: %d", + image->assembly->aname.name, image->assembly, reference->aname.name, reference, reference->ref_count); + } else { + if (image->assembly) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Failed to load assembly %s[%p]\n", + image->assembly->aname.name, image->assembly); + } + + image->references [index] = reference; + } + mono_assemblies_unlock (); + + if (image->references [index] != reference) { + /* Somebody loaded it before us */ + mono_assembly_close (reference); + } +} + +/** + * mono_assembly_load_references: + * @image: + * @status: + * @deprecated: There is no reason to use this method anymore, it does nothing + * + * This method is now a no-op, it does nothing other than setting the @status to #MONO_IMAGE_OK + */ +void +mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status) +{ + /* This is a no-op now but it is part of the embedding API so we can't remove it */ + *status = MONO_IMAGE_OK; +} + +typedef struct AssemblyLoadHook AssemblyLoadHook; +struct AssemblyLoadHook { + AssemblyLoadHook *next; + MonoAssemblyLoadFunc func; + gpointer user_data; +}; + +AssemblyLoadHook *assembly_load_hook = NULL; + +void +mono_assembly_invoke_load_hook (MonoAssembly *ass) +{ + AssemblyLoadHook *hook; + + for (hook = assembly_load_hook; hook; hook = hook->next) { + hook->func (ass, hook->user_data); + } +} + +void +mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data) +{ + AssemblyLoadHook *hook; + + g_return_if_fail (func != NULL); + + hook = g_new0 (AssemblyLoadHook, 1); + hook->func = func; + hook->user_data = user_data; + hook->next = assembly_load_hook; + assembly_load_hook = hook; +} + +static void +free_assembly_load_hooks (void) +{ + AssemblyLoadHook *hook, *next; + + for (hook = assembly_load_hook; hook; hook = next) { + next = hook->next; + g_free (hook); + } +} + +typedef struct AssemblySearchHook AssemblySearchHook; +struct AssemblySearchHook { + AssemblySearchHook *next; + MonoAssemblySearchFunc func; + gboolean refonly; + gboolean postload; + gpointer user_data; +}; + +AssemblySearchHook *assembly_search_hook = NULL; + +static MonoAssembly* +mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly, gboolean postload) +{ + AssemblySearchHook *hook; + + for (hook = assembly_search_hook; hook; hook = hook->next) { + if ((hook->refonly == refonly) && (hook->postload == postload)) { + MonoAssembly *ass; + /** + * A little explanation is in order here. + * + * The default postload search hook needs to know the requesting assembly to report it to managed code. + * The embedding API exposes a search hook that doesn't take such argument. + * + * The original fix would call the default search hook before all the registered ones and pass + * the requesting assembly to it. It works but broke a very suddle embedding API aspect that some users + * rely on. Which is the ordering between user hooks and the default runtime hook. + * + * Registering the hook after mono_jit_init would let your hook run before the default one and + * when using it to handle non standard app layouts this could save your app from a massive amount + * of syscalls that the default hook does when probing all sorts of places. Slow targets with horrible IO + * are all using this trick and if we broke this assumption they would be very disapointed at us. + * + * So what's the fix? We register the default hook using regular means and special case it when iterating + * over the registered hooks. This preserves ordering and enables managed resolve hooks to get the requesting + * assembly. + */ + if (hook->func == (void*)mono_domain_assembly_postload_search) + ass = mono_domain_assembly_postload_search (aname, requesting, refonly); + else + ass = hook->func (aname, hook->user_data); + if (ass) + return ass; + } + } + + return NULL; +} + +MonoAssembly* +mono_assembly_invoke_search_hook (MonoAssemblyName *aname) +{ + return mono_assembly_invoke_search_hook_internal (aname, NULL, FALSE, FALSE); +} + +static void +mono_install_assembly_search_hook_internal (MonoAssemblySearchFunc func, gpointer user_data, gboolean refonly, gboolean postload) +{ + AssemblySearchHook *hook; + + g_return_if_fail (func != NULL); + + hook = g_new0 (AssemblySearchHook, 1); + hook->func = func; + hook->user_data = user_data; + hook->refonly = refonly; + hook->postload = postload; + hook->next = assembly_search_hook; + assembly_search_hook = hook; +} + +void +mono_install_assembly_search_hook (MonoAssemblySearchFunc func, gpointer user_data) +{ + mono_install_assembly_search_hook_internal (func, user_data, FALSE, FALSE); +} + +static void +free_assembly_search_hooks (void) +{ + AssemblySearchHook *hook, *next; + + for (hook = assembly_search_hook; hook; hook = next) { + next = hook->next; + g_free (hook); + } +} + +void +mono_install_assembly_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data) +{ + mono_install_assembly_search_hook_internal (func, user_data, TRUE, FALSE); +} + +void +mono_install_assembly_postload_search_hook (MonoAssemblySearchFunc func, gpointer user_data) +{ + mono_install_assembly_search_hook_internal (func, user_data, FALSE, TRUE); +} + +void +mono_install_assembly_postload_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data) +{ + mono_install_assembly_search_hook_internal (func, user_data, TRUE, TRUE); +} + +typedef struct AssemblyPreLoadHook AssemblyPreLoadHook; +struct AssemblyPreLoadHook { + AssemblyPreLoadHook *next; + MonoAssemblyPreLoadFunc func; + gpointer user_data; +}; + +static AssemblyPreLoadHook *assembly_preload_hook = NULL; +static AssemblyPreLoadHook *assembly_refonly_preload_hook = NULL; + +static MonoAssembly * +invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path) +{ + AssemblyPreLoadHook *hook; + MonoAssembly *assembly; + + for (hook = assembly_preload_hook; hook; hook = hook->next) { + assembly = hook->func (aname, assemblies_path, hook->user_data); + if (assembly != NULL) + return assembly; + } + + return NULL; +} + +static MonoAssembly * +invoke_assembly_refonly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path) +{ + AssemblyPreLoadHook *hook; + MonoAssembly *assembly; + + for (hook = assembly_refonly_preload_hook; hook; hook = hook->next) { + assembly = hook->func (aname, assemblies_path, hook->user_data); + if (assembly != NULL) + return assembly; + } + + return NULL; +} + +void +mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data) +{ + AssemblyPreLoadHook *hook; + + g_return_if_fail (func != NULL); + + hook = g_new0 (AssemblyPreLoadHook, 1); + hook->func = func; + hook->user_data = user_data; + hook->next = assembly_preload_hook; + assembly_preload_hook = hook; +} + +void +mono_install_assembly_refonly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data) +{ + AssemblyPreLoadHook *hook; + + g_return_if_fail (func != NULL); + + hook = g_new0 (AssemblyPreLoadHook, 1); + hook->func = func; + hook->user_data = user_data; + hook->next = assembly_refonly_preload_hook; + assembly_refonly_preload_hook = hook; +} + +static void +free_assembly_preload_hooks (void) +{ + AssemblyPreLoadHook *hook, *next; + + for (hook = assembly_preload_hook; hook; hook = next) { + next = hook->next; + g_free (hook); + } + + for (hook = assembly_refonly_preload_hook; hook; hook = next) { + next = hook->next; + g_free (hook); + } +} + +static gchar * +absolute_dir (const gchar *filename) +{ + gchar *cwd; + gchar *mixed; + gchar **parts; + gchar *part; + GList *list, *tmp; + GString *result; + gchar *res; + gint i; + + if (g_path_is_absolute (filename)) { + part = g_path_get_dirname (filename); + res = g_strconcat (part, G_DIR_SEPARATOR_S, NULL); + g_free (part); + return res; + } + + cwd = g_get_current_dir (); + mixed = g_build_filename (cwd, filename, NULL); + parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0); + g_free (mixed); + g_free (cwd); + + list = NULL; + for (i = 0; (part = parts [i]) != NULL; i++) { + if (!strcmp (part, ".")) + continue; + + if (!strcmp (part, "..")) { + if (list && list->next) /* Don't remove root */ + list = g_list_delete_link (list, list); + } else { + list = g_list_prepend (list, part); + } + } + + result = g_string_new (""); + list = g_list_reverse (list); + + /* Ignores last data pointer, which should be the filename */ + for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next){ + if (tmp->data) + g_string_append_printf (result, "%s%c", (char *) tmp->data, + G_DIR_SEPARATOR); + } + + res = result->str; + g_string_free (result, FALSE); + g_list_free (list); + g_strfreev (parts); + if (*res == '\0') { + g_free (res); + return g_strdup ("."); + } + + return res; +} + +/** + * mono_assembly_open_from_bundle: + * @filename: Filename requested + * @status: return status code + * + * This routine tries to open the assembly specified by `filename' from the + * defined bundles, if found, returns the MonoImage for it, if not found + * returns NULL + */ +MonoImage * +mono_assembly_open_from_bundle (const char *filename, MonoImageOpenStatus *status, gboolean refonly) +{ + int i; + char *name; + MonoImage *image = NULL; + + /* + * we do a very simple search for bundled assemblies: it's not a general + * purpose assembly loading mechanism. + */ + + if (!bundles) + return NULL; + + name = g_path_get_basename (filename); + + mono_assemblies_lock (); + for (i = 0; !image && bundles [i]; ++i) { + if (strcmp (bundles [i]->name, name) == 0) { + image = mono_image_open_from_data_with_name ((char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly, name); + break; + } + } + mono_assemblies_unlock (); + if (image) { + mono_image_addref (image); + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Loader loaded assembly from bundle: '%s'.", name); + g_free (name); + return image; + } + g_free (name); + return NULL; +} + +/** + * mono_assemblies_open_full: + * @filename: the file to load + * @status: return status code + * @refonly: Whether this assembly is being opened in "reflection-only" mode. +* + * This loads an assembly from the specified @filename. The @filename allows + * a local URL (starting with a file:// prefix). If a file prefix is used, the + * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file + * is treated as a local path. + * + * First, an attempt is made to load the assembly from the bundled executable (for those + * deployments that have been done with the `mkbundle` tool or for scenarios where the + * assembly has been registered as an embedded assembly). If this is not the case, then + * the assembly is loaded from disk using `api:mono_image_open_full`. + * + * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of + * the assembly is made. + * + * If @refonly is set to true, then the assembly is loaded purely for inspection with + * the `System.Reflection` API. + * + * Returns: NULL on error, with the @status set to an error code, or a pointer + * to the assembly. + */ +MonoAssembly * +mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly) +{ + MonoImage *image; + MonoAssembly *ass; + MonoImageOpenStatus def_status; + gchar *fname; + gchar *new_fname; + gboolean loaded_from_bundle; + + g_return_val_if_fail (filename != NULL, NULL); + + if (!status) + status = &def_status; + *status = MONO_IMAGE_OK; + + if (strncmp (filename, "file://", 7) == 0) { + GError *error = NULL; + gchar *uri = (gchar *) filename; + gchar *tmpuri; + + /* + * MS allows file://c:/... and fails on file://localhost/c:/... + * They also throw an IndexOutOfRangeException if "file://" + */ + if (uri [7] != '/') + uri = g_strdup_printf ("file:///%s", uri + 7); + + tmpuri = uri; + uri = mono_escape_uri_string (tmpuri); + fname = g_filename_from_uri (uri, NULL, &error); + g_free (uri); + + if (tmpuri != filename) + g_free (tmpuri); + + if (error != NULL) { + g_warning ("%s\n", error->message); + g_error_free (error); + fname = g_strdup (filename); + } + } else { + fname = g_strdup (filename); + } + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "Assembly Loader probing location: '%s'.", fname); + + new_fname = NULL; + if (!mono_assembly_is_in_gac (fname)) { + MonoError error; + new_fname = mono_make_shadow_copy (fname, &error); + if (!is_ok (&error)) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "Assembly Loader shadow copy error: %s.", mono_error_get_message (&error)); + mono_error_cleanup (&error); + *status = MONO_IMAGE_IMAGE_INVALID; + g_free (fname); + return NULL; + } + } + if (new_fname && new_fname != fname) { + g_free (fname); + fname = new_fname; + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "Assembly Loader shadow-copied assembly to: '%s'.", fname); + } + + image = NULL; + + // If VM built with mkbundle + loaded_from_bundle = FALSE; + if (bundles != NULL) { + image = mono_assembly_open_from_bundle (fname, status, refonly); + loaded_from_bundle = image != NULL; + } + + if (!image) + image = mono_image_open_full (fname, status, refonly); + + if (!image){ + if (*status == MONO_IMAGE_OK) + *status = MONO_IMAGE_ERROR_ERRNO; + g_free (fname); + return NULL; + } + + if (image->assembly) { + /* Already loaded by another appdomain */ + mono_assembly_invoke_load_hook (image->assembly); + mono_image_close (image); + g_free (fname); + return image->assembly; + } + + ass = mono_assembly_load_from_full (image, fname, status, refonly); + + if (ass) { + if (!loaded_from_bundle) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, + "Assembly Loader loaded assembly from location: '%s'.", filename); + if (!refonly) + mono_config_for_assembly (ass->image); + } + + /* Clear the reference added by mono_image_open */ + mono_image_close (image); + + g_free (fname); + + return ass; +} + +static void +free_item (gpointer val, gpointer user_data) +{ + g_free (val); +} + +/** + * mono_assembly_load_friends: + * @ass: an assembly + * + * Load the list of friend assemblies that are allowed to access + * the assembly's internal types and members. They are stored as assembly + * names in custom attributes. + * + * This is an internal method, we need this because when we load mscorlib + * we do not have the internals visible cattr loaded yet, + * so we need to load these after we initialize the runtime. + * + * LOCKING: Acquires the assemblies lock plus the loader lock. + */ +void +mono_assembly_load_friends (MonoAssembly* ass) +{ + MonoError error; + int i; + MonoCustomAttrInfo* attrs; + GSList *list; + + if (ass->friend_assembly_names_inited) + return; + + attrs = mono_custom_attrs_from_assembly_checked (ass, &error); + mono_error_assert_ok (&error); + if (!attrs) { + mono_assemblies_lock (); + ass->friend_assembly_names_inited = TRUE; + mono_assemblies_unlock (); + return; + } + + mono_assemblies_lock (); + if (ass->friend_assembly_names_inited) { + mono_assemblies_unlock (); + return; + } + mono_assemblies_unlock (); + + list = NULL; + /* + * We build the list outside the assemblies lock, the worse that can happen + * is that we'll need to free the allocated list. + */ + for (i = 0; i < attrs->num_attrs; ++i) { + MonoCustomAttrEntry *attr = &attrs->attrs [i]; + MonoAssemblyName *aname; + const gchar *data; + /* Do some sanity checking */ + if (!attr->ctor || attr->ctor->klass != mono_class_try_get_internals_visible_class ()) + continue; + if (attr->data_size < 4) + continue; + data = (const char*)attr->data; + /* 0xFF means null string, see custom attr format */ + if (data [0] != 1 || data [1] != 0 || (data [2] & 0xFF) == 0xFF) + continue; + mono_metadata_decode_value (data + 2, &data); + aname = g_new0 (MonoAssemblyName, 1); + /*g_print ("friend ass: %s\n", data);*/ + if (mono_assembly_name_parse_full (data, aname, TRUE, NULL, NULL)) { + list = g_slist_prepend (list, aname); + } else { + g_free (aname); + } + } + mono_custom_attrs_free (attrs); + + mono_assemblies_lock (); + if (ass->friend_assembly_names_inited) { + mono_assemblies_unlock (); + g_slist_foreach (list, free_item, NULL); + g_slist_free (list); + return; + } + ass->friend_assembly_names = list; + + /* Because of the double checked locking pattern above */ + mono_memory_barrier (); + ass->friend_assembly_names_inited = TRUE; + mono_assemblies_unlock (); +} + +/** + * mono_assembly_open: + * @filename: Opens the assembly pointed out by this name + * @status: return status code + * + * This loads an assembly from the specified @filename. The @filename allows + * a local URL (starting with a file:// prefix). If a file prefix is used, the + * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file + * is treated as a local path. + * + * First, an attempt is made to load the assembly from the bundled executable (for those + * deployments that have been done with the `mkbundle` tool or for scenarios where the + * assembly has been registered as an embedded assembly). If this is not the case, then + * the assembly is loaded from disk using `api:mono_image_open_full`. + * + * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of + * the assembly is made. + * + * Return: a pointer to the MonoAssembly if @filename contains a valid + * assembly or NULL on error. Details about the error are stored in the + * @status variable. + */ +MonoAssembly * +mono_assembly_open (const char *filename, MonoImageOpenStatus *status) +{ + return mono_assembly_open_full (filename, status, FALSE); +} + +/** + * mono_assembly_load_from_full: + * @image: Image to load the assembly from + * @fname: assembly name to associate with the assembly + * @status: returns the status condition + * @refonly: Whether this assembly is being opened in "reflection-only" mode. + * + * If the provided @image has an assembly reference, it will process the given + * image as an assembly with the given name. + * + * Most likely you want to use the `api:mono_assembly_load_full` method instead. + * + * Returns: A valid pointer to a `MonoAssembly*` on success and the @status will be + * set to #MONO_IMAGE_OK; or NULL on error. + * + * If there is an error loading the assembly the @status will indicate the + * reason with @status being set to `MONO_IMAGE_INVALID` if the + * image did not contain an assembly reference table. + */ +MonoAssembly * +mono_assembly_load_from_full (MonoImage *image, const char*fname, + MonoImageOpenStatus *status, gboolean refonly) +{ + MonoAssembly *ass, *ass2; + char *base_dir; + + if (!image->tables [MONO_TABLE_ASSEMBLY].rows) { + /* 'image' doesn't have a manifest -- maybe someone is trying to Assembly.Load a .netmodule */ + *status = MONO_IMAGE_IMAGE_INVALID; + return NULL; + } + +#if defined (HOST_WIN32) + { + gchar *tmp_fn; + int i; + + tmp_fn = g_strdup (fname); + for (i = strlen (tmp_fn) - 1; i >= 0; i--) { + if (tmp_fn [i] == '/') + tmp_fn [i] = '\\'; + } + + base_dir = absolute_dir (tmp_fn); + g_free (tmp_fn); + } +#else + base_dir = absolute_dir (fname); +#endif + + /* + * Create assembly struct, and enter it into the assembly cache + */ + ass = g_new0 (MonoAssembly, 1); + ass->basedir = base_dir; + ass->ref_only = refonly; + ass->image = image; + + mono_profiler_assembly_event (ass, MONO_PROFILE_START_LOAD); + + mono_assembly_fill_assembly_name (image, &ass->aname); + + if (mono_defaults.corlib && strcmp (ass->aname.name, "mscorlib") == 0) { + // MS.NET doesn't support loading other mscorlibs + g_free (ass); + g_free (base_dir); + mono_image_addref (mono_defaults.corlib); + *status = MONO_IMAGE_OK; + return mono_defaults.corlib->assembly; + } + + /* Add a non-temporary reference because of ass->image */ + mono_image_addref (image); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image addref %s[%p] -> %s[%p]: %d", ass->aname.name, ass, image->name, image, image->ref_count); + + /* + * The load hooks might take locks so we can't call them while holding the + * assemblies lock. + */ + if (ass->aname.name) { + ass2 = mono_assembly_invoke_search_hook_internal (&ass->aname, NULL, refonly, FALSE); + if (ass2) { + g_free (ass); + g_free (base_dir); + mono_image_close (image); + *status = MONO_IMAGE_OK; + return ass2; + } + } + + mono_assemblies_lock (); + + if (image->assembly) { + /* + * This means another thread has already loaded the assembly, but not yet + * called the load hooks so the search hook can't find the assembly. + */ + mono_assemblies_unlock (); + ass2 = image->assembly; + g_free (ass); + g_free (base_dir); + mono_image_close (image); + *status = MONO_IMAGE_OK; + return ass2; + } + + image->assembly = ass; + + loaded_assemblies = g_list_prepend (loaded_assemblies, ass); + mono_assemblies_unlock (); + +#ifdef HOST_WIN32 + if (image->is_module_handle) + mono_image_fixup_vtable (image); +#endif + + mono_assembly_invoke_load_hook (ass); + + mono_profiler_assembly_loaded (ass, MONO_PROFILE_OK); + + return ass; +} + +/** + * mono_assembly_load_from: + * @image: Image to load the assembly from + * @fname: assembly name to associate with the assembly + * @status: return status code + * + * If the provided @image has an assembly reference, it will process the given + * image as an assembly with the given name. + * + * Most likely you want to use the `api:mono_assembly_load_full` method instead. + * + * This is equivalent to calling `api:mono_assembly_load_from_full` with the + * @refonly parameter set to FALSE. + * Returns: A valid pointer to a `MonoAssembly*` on success and the @status will be + * set to #MONO_IMAGE_OK; or NULL on error. + * + * If there is an error loading the assembly the @status will indicate the + * reason with @status being set to `MONO_IMAGE_INVALID` if the + * image did not contain an assembly reference table. + + */ +MonoAssembly * +mono_assembly_load_from (MonoImage *image, const char *fname, + MonoImageOpenStatus *status) +{ + return mono_assembly_load_from_full (image, fname, status, FALSE); +} + +/** + * mono_assembly_name_free: + * @aname: assembly name to free + * + * Frees the provided assembly name object. + * (it does not frees the object itself, only the name members). + */ +void +mono_assembly_name_free (MonoAssemblyName *aname) +{ + if (aname == NULL) + return; + + g_free ((void *) aname->name); + g_free ((void *) aname->culture); + g_free ((void *) aname->hash_value); +} + +static gboolean +parse_public_key (const gchar *key, gchar** pubkey, gboolean *is_ecma) +{ + const gchar *pkey; + gchar header [16], val, *arr; + gint i, j, offset, bitlen, keylen, pkeylen; + + keylen = strlen (key) >> 1; + if (keylen < 1) + return FALSE; + + /* allow the ECMA standard key */ + if (strcmp (key, "00000000000000000400000000000000") == 0) { + if (pubkey) { + *pubkey = g_strdup (key); + *is_ecma = TRUE; + } + return TRUE; + } + *is_ecma = FALSE; + val = g_ascii_xdigit_value (key [0]) << 4; + val |= g_ascii_xdigit_value (key [1]); + switch (val) { + case 0x00: + if (keylen < 13) + return FALSE; + val = g_ascii_xdigit_value (key [24]); + val |= g_ascii_xdigit_value (key [25]); + if (val != 0x06) + return FALSE; + pkey = key + 24; + break; + case 0x06: + pkey = key; + break; + default: + return FALSE; + } + + /* We need the first 16 bytes + * to check whether this key is valid or not */ + pkeylen = strlen (pkey) >> 1; + if (pkeylen < 16) + return FALSE; + + for (i = 0, j = 0; i < 16; i++) { + header [i] = g_ascii_xdigit_value (pkey [j++]) << 4; + header [i] |= g_ascii_xdigit_value (pkey [j++]); + } + + if (header [0] != 0x06 || /* PUBLICKEYBLOB (0x06) */ + header [1] != 0x02 || /* Version (0x02) */ + header [2] != 0x00 || /* Reserved (word) */ + header [3] != 0x00 || + (guint)(read32 (header + 8)) != 0x31415352) /* DWORD magic = RSA1 */ + return FALSE; + + /* Based on this length, we _should_ be able to know if the length is right */ + bitlen = read32 (header + 12) >> 3; + if ((bitlen + 16 + 4) != pkeylen) + return FALSE; + + /* parsing is OK and the public key itself is not requested back */ + if (!pubkey) + return TRUE; + + /* Encode the size of the blob */ + offset = 0; + if (keylen <= 127) { + arr = (gchar *)g_malloc (keylen + 1); + arr [offset++] = keylen; + } else { + arr = (gchar *)g_malloc (keylen + 2); + arr [offset++] = 0x80; /* 10bs */ + arr [offset++] = keylen; + } + + for (i = offset, j = 0; i < keylen + offset; i++) { + arr [i] = g_ascii_xdigit_value (key [j++]) << 4; + arr [i] |= g_ascii_xdigit_value (key [j++]); + } + + *pubkey = arr; + + return TRUE; +} + +static gboolean +build_assembly_name (const char *name, const char *version, const char *culture, const char *token, const char *key, guint32 flags, guint32 arch, MonoAssemblyName *aname, gboolean save_public_key) +{ + gint major, minor, build, revision; + gint len; + gint version_parts; + gchar *pkey, *pkeyptr, *encoded, tok [8]; + + memset (aname, 0, sizeof (MonoAssemblyName)); + + if (version) { + version_parts = sscanf (version, "%u.%u.%u.%u", &major, &minor, &build, &revision); + if (version_parts < 2 || version_parts > 4) + return FALSE; + + /* FIXME: we should set build & revision to -1 (instead of 0) + if these are not set in the version string. That way, later on, + we can still determine if these were specified. */ + aname->major = major; + aname->minor = minor; + if (version_parts >= 3) + aname->build = build; + else + aname->build = 0; + if (version_parts == 4) + aname->revision = revision; + else + aname->revision = 0; + } + + aname->flags = flags; + aname->arch = arch; + aname->name = g_strdup (name); + + if (culture) { + if (g_ascii_strcasecmp (culture, "neutral") == 0) + aname->culture = g_strdup (""); + else + aname->culture = g_strdup (culture); + } + + if (token && strncmp (token, "null", 4) != 0) { + char *lower; + + /* the constant includes the ending NULL, hence the -1 */ + if (strlen (token) != (MONO_PUBLIC_KEY_TOKEN_LENGTH - 1)) { + mono_assembly_name_free (aname); + return FALSE; + } + lower = g_ascii_strdown (token, MONO_PUBLIC_KEY_TOKEN_LENGTH); + g_strlcpy ((char*)aname->public_key_token, lower, MONO_PUBLIC_KEY_TOKEN_LENGTH); + g_free (lower); + } + + if (key) { + gboolean is_ecma; + if (strcmp (key, "null") == 0 || !parse_public_key (key, &pkey, &is_ecma)) { + mono_assembly_name_free (aname); + return FALSE; + } + + if (is_ecma) { + if (save_public_key) + aname->public_key = (guint8*)pkey; + else + g_free (pkey); + g_strlcpy ((gchar*)aname->public_key_token, "b77a5c561934e089", MONO_PUBLIC_KEY_TOKEN_LENGTH); + return TRUE; + } + + len = mono_metadata_decode_blob_size ((const gchar *) pkey, (const gchar **) &pkeyptr); + // We also need to generate the key token + mono_digest_get_public_token ((guchar*) tok, (guint8*) pkeyptr, len); + encoded = encode_public_tok ((guchar*) tok, 8); + g_strlcpy ((gchar*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH); + g_free (encoded); + + if (save_public_key) + aname->public_key = (guint8*) pkey; + else + g_free (pkey); + } + + return TRUE; +} + +static gboolean +parse_assembly_directory_name (const char *name, const char *dirname, MonoAssemblyName *aname) +{ + gchar **parts; + gboolean res; + + parts = g_strsplit (dirname, "_", 3); + if (!parts || !parts[0] || !parts[1] || !parts[2]) { + g_strfreev (parts); + return FALSE; + } + + res = build_assembly_name (name, parts[0], parts[1], parts[2], NULL, 0, 0, aname, FALSE); + g_strfreev (parts); + return res; +} + +static gboolean +split_key_value (const gchar *pair, gchar **key, guint32 *keylen, gchar **value) +{ + char *eqsign = strchr (pair, '='); + if (!eqsign) { + *key = NULL; + *keylen = 0; + *value = NULL; + return FALSE; + } + + *key = (gchar*)pair; + *keylen = eqsign - *key; + while (*keylen > 0 && g_ascii_isspace ((*key) [*keylen - 1])) + (*keylen)--; + *value = g_strstrip (eqsign + 1); + return TRUE; +} + +gboolean +mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboolean save_public_key, gboolean *is_version_defined, gboolean *is_token_defined) +{ + gchar *dllname; + gchar *dllname_uq; + gchar *version = NULL; + gchar *version_uq; + gchar *culture = NULL; + gchar *culture_uq; + gchar *token = NULL; + gchar *token_uq; + gchar *key = NULL; + gchar *key_uq; + gchar *retargetable = NULL; + gchar *retargetable_uq; + gchar *procarch; + gchar *procarch_uq; + gboolean res; + gchar *value, *part_name; + guint32 part_name_len; + gchar **parts; + gchar **tmp; + gboolean version_defined; + gboolean token_defined; + guint32 flags = 0; + guint32 arch = MONO_PROCESSOR_ARCHITECTURE_NONE; + + if (!is_version_defined) + is_version_defined = &version_defined; + *is_version_defined = FALSE; + if (!is_token_defined) + is_token_defined = &token_defined; + *is_token_defined = FALSE; + + parts = tmp = g_strsplit (name, ",", 6); + if (!tmp || !*tmp) { + g_strfreev (tmp); + return FALSE; + } + + dllname = g_strstrip (*tmp); + + tmp++; + + while (*tmp) { + if (!split_key_value (g_strstrip (*tmp), &part_name, &part_name_len, &value)) + goto cleanup_and_fail; + + if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Version", part_name_len)) { + *is_version_defined = TRUE; + version = value; + if (strlen (version) == 0) { + goto cleanup_and_fail; + } + tmp++; + continue; + } + + if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Culture", part_name_len)) { + culture = value; + if (strlen (culture) == 0) { + goto cleanup_and_fail; + } + tmp++; + continue; + } + + if (part_name_len == 14 && !g_ascii_strncasecmp (part_name, "PublicKeyToken", part_name_len)) { + *is_token_defined = TRUE; + token = value; + if (strlen (token) == 0) { + goto cleanup_and_fail; + } + tmp++; + continue; + } + + if (part_name_len == 9 && !g_ascii_strncasecmp (part_name, "PublicKey", part_name_len)) { + key = value; + if (strlen (key) == 0) { + goto cleanup_and_fail; + } + tmp++; + continue; + } + + if (part_name_len == 12 && !g_ascii_strncasecmp (part_name, "Retargetable", part_name_len)) { + retargetable = value; + retargetable_uq = unquote (retargetable); + if (retargetable_uq != NULL) + retargetable = retargetable_uq; + + if (!g_ascii_strcasecmp (retargetable, "yes")) { + flags |= ASSEMBLYREF_RETARGETABLE_FLAG; + } else if (g_ascii_strcasecmp (retargetable, "no")) { + free (retargetable_uq); + goto cleanup_and_fail; + } + + free (retargetable_uq); + tmp++; + continue; + } + + if (part_name_len == 21 && !g_ascii_strncasecmp (part_name, "ProcessorArchitecture", part_name_len)) { + procarch = value; + procarch_uq = unquote (procarch); + if (procarch_uq != NULL) + procarch = procarch_uq; + + if (!g_ascii_strcasecmp (procarch, "MSIL")) + arch = MONO_PROCESSOR_ARCHITECTURE_MSIL; + else if (!g_ascii_strcasecmp (procarch, "X86")) + arch = MONO_PROCESSOR_ARCHITECTURE_X86; + else if (!g_ascii_strcasecmp (procarch, "IA64")) + arch = MONO_PROCESSOR_ARCHITECTURE_IA64; + else if (!g_ascii_strcasecmp (procarch, "AMD64")) + arch = MONO_PROCESSOR_ARCHITECTURE_AMD64; + else { + free (procarch_uq); + goto cleanup_and_fail; + } + + free (procarch_uq); + tmp++; + continue; + } + + g_strfreev (parts); + return FALSE; + } + + /* if retargetable flag is set, then we must have a fully qualified name */ + if (retargetable != NULL && (version == NULL || culture == NULL || (key == NULL && token == NULL))) { + goto cleanup_and_fail; + } + + dllname_uq = unquote (dllname); + version_uq = unquote (version); + culture_uq = unquote (culture); + token_uq = unquote (token); + key_uq = unquote (key); + + res = build_assembly_name ( + dllname_uq == NULL ? dllname : dllname_uq, + version_uq == NULL ? version : version_uq, + culture_uq == NULL ? culture : culture_uq, + token_uq == NULL ? token : token_uq, + key_uq == NULL ? key : key_uq, + flags, arch, aname, save_public_key); + + free (dllname_uq); + free (version_uq); + free (culture_uq); + free (token_uq); + free (key_uq); + + g_strfreev (parts); + return res; + +cleanup_and_fail: + g_strfreev (parts); + return FALSE; +} + +static char* +unquote (const char *str) +{ + gint slen; + const char *end; + + if (str == NULL) + return NULL; + + slen = strlen (str); + if (slen < 2) + return NULL; + + if (*str != '\'' && *str != '\"') + return NULL; + + end = str + slen - 1; + if (*str != *end) + return NULL; + + return g_strndup (str + 1, slen - 2); +} + +/** + * mono_assembly_name_parse: + * @name: name to parse + * @aname: the destination assembly name + * + * Parses an assembly qualified type name and assigns the name, + * version, culture and token to the provided assembly name object. + * + * Returns: TRUE if the name could be parsed. + */ +gboolean +mono_assembly_name_parse (const char *name, MonoAssemblyName *aname) +{ + return mono_assembly_name_parse_full (name, aname, FALSE, NULL, NULL); +} + +/** + * mono_assembly_name_new: + * @name: name to parse + * + * Allocate a new MonoAssemblyName and fill its values from the + * passed @name. + * + * Returns: a newly allocated structure or NULL if there was any failure. + */ +MonoAssemblyName* +mono_assembly_name_new (const char *name) +{ + MonoAssemblyName *aname = g_new0 (MonoAssemblyName, 1); + if (mono_assembly_name_parse (name, aname)) + return aname; + g_free (aname); + return NULL; +} + +const char* +mono_assembly_name_get_name (MonoAssemblyName *aname) +{ + return aname->name; +} + +const char* +mono_assembly_name_get_culture (MonoAssemblyName *aname) +{ + return aname->culture; +} + +mono_byte* +mono_assembly_name_get_pubkeytoken (MonoAssemblyName *aname) +{ + if (aname->public_key_token [0]) + return aname->public_key_token; + return NULL; +} + +uint16_t +mono_assembly_name_get_version (MonoAssemblyName *aname, uint16_t *minor, uint16_t *build, uint16_t *revision) +{ + if (minor) + *minor = aname->minor; + if (build) + *build = aname->build; + if (revision) + *revision = aname->revision; + return aname->major; +} + +static MonoAssembly* +probe_for_partial_name (const char *basepath, const char *fullname, MonoAssemblyName *aname, MonoImageOpenStatus *status) +{ + gchar *fullpath = NULL; + GDir *dirhandle; + const char* direntry; + MonoAssemblyName gac_aname; + gint major=-1, minor=0, build=0, revision=0; + gboolean exact_version; + + dirhandle = g_dir_open (basepath, 0, NULL); + if (!dirhandle) + return NULL; + + exact_version = (aname->major | aname->minor | aname->build | aname->revision) != 0; + + while ((direntry = g_dir_read_name (dirhandle))) { + gboolean match = TRUE; + + if(!parse_assembly_directory_name (aname->name, direntry, &gac_aname)) + continue; + + if (aname->culture != NULL && strcmp (aname->culture, gac_aname.culture) != 0) + match = FALSE; + + if (match && strlen ((char*)aname->public_key_token) > 0 && + !mono_public_tokens_are_equal (aname->public_key_token, gac_aname.public_key_token)) + match = FALSE; + + if (match) { + if (exact_version) { + match = (aname->major == gac_aname.major && aname->minor == gac_aname.minor && + aname->build == gac_aname.build && aname->revision == gac_aname.revision); + } + else if (gac_aname.major < major) + match = FALSE; + else if (gac_aname.major == major) { + if (gac_aname.minor < minor) + match = FALSE; + else if (gac_aname.minor == minor) { + if (gac_aname.build < build) + match = FALSE; + else if (gac_aname.build == build && gac_aname.revision <= revision) + match = FALSE; + } + } + } + + if (match) { + major = gac_aname.major; + minor = gac_aname.minor; + build = gac_aname.build; + revision = gac_aname.revision; + g_free (fullpath); + fullpath = g_build_path (G_DIR_SEPARATOR_S, basepath, direntry, fullname, NULL); + } + + mono_assembly_name_free (&gac_aname); + } + + g_dir_close (dirhandle); + + if (fullpath == NULL) + return NULL; + else { + MonoAssembly *res = mono_assembly_open (fullpath, status); + g_free (fullpath); + return res; + } +} + +/** + * mono_assembly_load_with_partial_name: + * @name: an assembly name that is then parsed by `api:mono_assembly_name_parse`. + * @status: return status code + * + * Loads a Mono Assembly from a name. The name is parsed using `api:mono_assembly_name_parse`, + * so it might contain a qualified type name, version, culture and token. + * + * This will load the assembly from the file whose name is derived from the assembly name + * by appending the .dll extension. + * + * The assembly is loaded from either one of the extra Global Assembly Caches specified + * by the extra GAC paths (specified by the `MONO_GAC_PREFIX` environment variable) or + * if that fails from the GAC. + * + * Returns: NULL on failure, or a pointer to a MonoAssembly on success. + */ +MonoAssembly* +mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status) +{ + MonoError error; + MonoAssembly *res; + MonoAssemblyName *aname, base_name; + MonoAssemblyName mapped_aname; + gchar *fullname, *gacpath; + gchar **paths; + + memset (&base_name, 0, sizeof (MonoAssemblyName)); + aname = &base_name; + + if (!mono_assembly_name_parse (name, aname)) + return NULL; + + /* + * If no specific version has been requested, make sure we load the + * correct version for system assemblies. + */ + if ((aname->major | aname->minor | aname->build | aname->revision) == 0) + aname = mono_assembly_remap_version (aname, &mapped_aname); + + res = mono_assembly_loaded (aname); + if (res) { + mono_assembly_name_free (aname); + return res; + } + + res = invoke_assembly_preload_hook (aname, assemblies_path); + if (res) { + res->in_gac = FALSE; + mono_assembly_name_free (aname); + return res; + } + + fullname = g_strdup_printf ("%s.dll", aname->name); + + if (extra_gac_paths) { + paths = extra_gac_paths; + while (!res && *paths) { + gacpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", aname->name, NULL); + res = probe_for_partial_name (gacpath, fullname, aname, status); + g_free (gacpath); + paths++; + } + } + + if (res) { + res->in_gac = TRUE; + g_free (fullname); + mono_assembly_name_free (aname); + return res; + } + + gacpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), "mono", "gac", aname->name, NULL); + res = probe_for_partial_name (gacpath, fullname, aname, status); + g_free (gacpath); + + if (res) + res->in_gac = TRUE; + else { + MonoDomain *domain = mono_domain_get (); + MonoReflectionAssembly *refasm; + + refasm = mono_try_assembly_resolve (domain, mono_string_new (domain, name), NULL, FALSE, &error); + if (!is_ok (&error)) { + g_free (fullname); + mono_assembly_name_free (aname); + mono_error_cleanup (&error); + if (*status == MONO_IMAGE_OK) + *status = MONO_IMAGE_IMAGE_INVALID; + } + + if (refasm) + res = refasm->assembly; + } + + g_free (fullname); + mono_assembly_name_free (aname); + + return res; +} + +static MonoBoolean +mono_assembly_is_in_gac (const gchar *filename) +{ + const gchar *rootdir; + gchar *gp; + gchar **paths; + + if (filename == NULL) + return FALSE; + + for (paths = extra_gac_paths; paths && *paths; paths++) { + if (strstr (*paths, filename) != *paths) + continue; + + gp = (gchar *) (filename + strlen (*paths)); + if (*gp != G_DIR_SEPARATOR) + continue; + gp++; + if (strncmp (gp, "lib", 3)) + continue; + gp += 3; + if (*gp != G_DIR_SEPARATOR) + continue; + gp++; + if (strncmp (gp, "mono", 4)) + continue; + gp += 4; + if (*gp != G_DIR_SEPARATOR) + continue; + gp++; + if (strncmp (gp, "gac", 3)) + continue; + gp += 3; + if (*gp != G_DIR_SEPARATOR) + continue; + + return TRUE; + } + + rootdir = mono_assembly_getrootdir (); + if (strstr (filename, rootdir) != filename) + return FALSE; + + gp = (gchar *) (filename + strlen (rootdir)); + if (*gp != G_DIR_SEPARATOR) + return FALSE; + gp++; + if (strncmp (gp, "mono", 4)) + return FALSE; + gp += 4; + if (*gp != G_DIR_SEPARATOR) + return FALSE; + gp++; + if (strncmp (gp, "gac", 3)) + return FALSE; + gp += 3; + if (*gp != G_DIR_SEPARATOR) + return FALSE; + return TRUE; +} + +static MonoImage* +mono_assembly_load_publisher_policy (MonoAssemblyName *aname) +{ + MonoImage *image; + gchar *filename, *pname, *name, *culture, *version, *fullpath, *subpath; + gchar **paths; + gint32 len; + + if (strstr (aname->name, ".dll")) { + len = strlen (aname->name) - 4; + name = (gchar *)g_malloc (len + 1); + strncpy (name, aname->name, len); + name[len] = 0; + } else + name = g_strdup (aname->name); + + if (aname->culture) + culture = g_utf8_strdown (aname->culture, -1); + else + culture = g_strdup (""); + + pname = g_strdup_printf ("policy.%d.%d.%s", aname->major, aname->minor, name); + version = g_strdup_printf ("0.0.0.0_%s_%s", culture, aname->public_key_token); + g_free (name); + g_free (culture); + + filename = g_strconcat (pname, ".dll", NULL); + subpath = g_build_path (G_DIR_SEPARATOR_S, pname, version, filename, NULL); + g_free (pname); + g_free (version); + g_free (filename); + + image = NULL; + if (extra_gac_paths) { + paths = extra_gac_paths; + while (!image && *paths) { + fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, + "lib", "mono", "gac", subpath, NULL); + image = mono_image_open (fullpath, NULL); + g_free (fullpath); + paths++; + } + } + + if (image) { + g_free (subpath); + return image; + } + + fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), + "mono", "gac", subpath, NULL); + image = mono_image_open (fullpath, NULL); + g_free (subpath); + g_free (fullpath); + + return image; +} + +static MonoAssemblyName* +mono_assembly_bind_version (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname, MonoAssemblyName *dest_name) +{ + memcpy (dest_name, aname, sizeof (MonoAssemblyName)); + dest_name->major = info->new_version.major; + dest_name->minor = info->new_version.minor; + dest_name->build = info->new_version.build; + dest_name->revision = info->new_version.revision; + + return dest_name; +} + +/* LOCKING: assembly_binding lock must be held */ +static MonoAssemblyBindingInfo* +search_binding_loaded (MonoAssemblyName *aname) +{ + GSList *tmp; + + for (tmp = loaded_assembly_bindings; tmp; tmp = tmp->next) { + MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)tmp->data; + if (assembly_binding_maps_name (info, aname)) + return info; + } + + return NULL; +} + +static inline gboolean +info_compare_versions (AssemblyVersionSet *left, AssemblyVersionSet *right) +{ + if (left->major != right->major || left->minor != right->minor || + left->build != right->build || left->revision != right->revision) + return FALSE; + + return TRUE; +} + +static inline gboolean +info_versions_equal (MonoAssemblyBindingInfo *left, MonoAssemblyBindingInfo *right) +{ + if (left->has_old_version_bottom != right->has_old_version_bottom) + return FALSE; + + if (left->has_old_version_top != right->has_old_version_top) + return FALSE; + + if (left->has_new_version != right->has_new_version) + return FALSE; + + if (left->has_old_version_bottom && !info_compare_versions (&left->old_version_bottom, &right->old_version_bottom)) + return FALSE; + + if (left->has_old_version_top && !info_compare_versions (&left->old_version_top, &right->old_version_top)) + return FALSE; + + if (left->has_new_version && !info_compare_versions (&left->new_version, &right->new_version)) + return FALSE; + + return TRUE; +} + +/* LOCKING: assumes all the necessary locks are held */ +static void +assembly_binding_info_parsed (MonoAssemblyBindingInfo *info, void *user_data) +{ + MonoAssemblyBindingInfo *info_copy; + GSList *tmp; + MonoAssemblyBindingInfo *info_tmp; + MonoDomain *domain = (MonoDomain*)user_data; + + if (!domain) + return; + + for (tmp = domain->assembly_bindings; tmp; tmp = tmp->next) { + info_tmp = (MonoAssemblyBindingInfo *)tmp->data; + if (strcmp (info->name, info_tmp->name) == 0 && info_versions_equal (info, info_tmp)) + return; + } + + info_copy = (MonoAssemblyBindingInfo *)mono_mempool_alloc0 (domain->mp, sizeof (MonoAssemblyBindingInfo)); + memcpy (info_copy, info, sizeof (MonoAssemblyBindingInfo)); + if (info->name) + info_copy->name = mono_mempool_strdup (domain->mp, info->name); + if (info->culture) + info_copy->culture = mono_mempool_strdup (domain->mp, info->culture); + + domain->assembly_bindings = g_slist_append_mempool (domain->mp, domain->assembly_bindings, info_copy); +} + +static int +get_version_number (int major, int minor) +{ + return major * 256 + minor; +} + +static inline gboolean +info_major_minor_in_range (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname) +{ + int aname_version_number = get_version_number (aname->major, aname->minor); + if (!info->has_old_version_bottom) + return FALSE; + + if (get_version_number (info->old_version_bottom.major, info->old_version_bottom.minor) > aname_version_number) + return FALSE; + + if (info->has_old_version_top && get_version_number (info->old_version_top.major, info->old_version_top.minor) < aname_version_number) + return FALSE; + + /* This is not the nicest way to do it, but it's a by-product of the way parsing is done */ + info->major = aname->major; + info->minor = aname->minor; + + return TRUE; +} + +/* LOCKING: Assumes that we are already locked - both loader and domain locks */ +static MonoAssemblyBindingInfo* +get_per_domain_assembly_binding_info (MonoDomain *domain, MonoAssemblyName *aname) +{ + MonoAssemblyBindingInfo *info; + GSList *list; + + if (!domain->assembly_bindings) + return NULL; + + info = NULL; + for (list = domain->assembly_bindings; list; list = list->next) { + info = (MonoAssemblyBindingInfo *)list->data; + if (info && !strcmp (aname->name, info->name) && info_major_minor_in_range (info, aname)) + break; + info = NULL; + } + + if (info) { + if (info->name && info->public_key_token [0] && info->has_old_version_bottom && + info->has_new_version && assembly_binding_maps_name (info, aname)) + info->is_valid = TRUE; + else + info->is_valid = FALSE; + } + + return info; +} + +static MonoAssemblyName* +mono_assembly_apply_binding (MonoAssemblyName *aname, MonoAssemblyName *dest_name) +{ + MonoError error; + MonoAssemblyBindingInfo *info, *info2; + MonoImage *ppimage; + MonoDomain *domain; + + if (aname->public_key_token [0] == 0) + return aname; + + domain = mono_domain_get (); + + mono_assembly_binding_lock (); + info = search_binding_loaded (aname); + mono_assembly_binding_unlock (); + + if (!info) { + mono_domain_lock (domain); + info = get_per_domain_assembly_binding_info (domain, aname); + mono_domain_unlock (domain); + } + + if (info) { + if (!check_policy_versions (info, aname)) + return aname; + + mono_assembly_bind_version (info, aname, dest_name); + return dest_name; + } + + if (domain && domain->setup && domain->setup->configuration_file) { + mono_domain_lock (domain); + if (!domain->assembly_bindings_parsed) { + gchar *domain_config_file_name = mono_string_to_utf8_checked (domain->setup->configuration_file, &error); + mono_error_raise_exception (&error); /* FIXME don't raise here */ + + gchar *domain_config_file_path = mono_portability_find_file (domain_config_file_name, TRUE); + + if (!domain_config_file_path) + domain_config_file_path = domain_config_file_name; + + mono_config_parse_assembly_bindings (domain_config_file_path, aname->major, aname->minor, domain, assembly_binding_info_parsed); + domain->assembly_bindings_parsed = TRUE; + if (domain_config_file_name != domain_config_file_path) + g_free (domain_config_file_name); + g_free (domain_config_file_path); + } + + info2 = get_per_domain_assembly_binding_info (domain, aname); + + if (info2) { + info = (MonoAssemblyBindingInfo *)g_memdup (info2, sizeof (MonoAssemblyBindingInfo)); + info->name = g_strdup (info2->name); + info->culture = g_strdup (info2->culture); + info->domain_id = domain->domain_id; + } + + mono_domain_unlock (domain); + } + + if (!info) { + info = g_new0 (MonoAssemblyBindingInfo, 1); + info->major = aname->major; + info->minor = aname->minor; + } + + if (!info->is_valid) { + ppimage = mono_assembly_load_publisher_policy (aname); + if (ppimage) { + get_publisher_policy_info (ppimage, aname, info); + mono_image_close (ppimage); + } + } + + /* Define default error value if needed */ + if (!info->is_valid) { + info->name = g_strdup (aname->name); + info->culture = g_strdup (aname->culture); + g_strlcpy ((char *)info->public_key_token, (const char *)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH); + } + + mono_assembly_binding_lock (); + info2 = search_binding_loaded (aname); + if (info2) { + /* This binding was added by another thread + * before us */ + mono_assembly_binding_info_free (info); + g_free (info); + + info = info2; + } else + loaded_assembly_bindings = g_slist_prepend (loaded_assembly_bindings, info); + + mono_assembly_binding_unlock (); + + if (!info->is_valid || !check_policy_versions (info, aname)) + return aname; + + mono_assembly_bind_version (info, aname, dest_name); + return dest_name; +} + +/** + * mono_assembly_load_from_gac + * + * @aname: The assembly name object + */ +static MonoAssembly* +mono_assembly_load_from_gac (MonoAssemblyName *aname, gchar *filename, MonoImageOpenStatus *status, MonoBoolean refonly) +{ + MonoAssembly *result = NULL; + gchar *name, *version, *culture, *fullpath, *subpath; + gint32 len; + gchar **paths; + char *pubtok; + + if (aname->public_key_token [0] == 0) { + return NULL; + } + + if (strstr (aname->name, ".dll")) { + len = strlen (filename) - 4; + name = (gchar *)g_malloc (len + 1); + strncpy (name, aname->name, len); + name[len] = 0; + } else { + name = g_strdup (aname->name); + } + + if (aname->culture) { + culture = g_utf8_strdown (aname->culture, -1); + } else { + culture = g_strdup (""); + } + + pubtok = g_ascii_strdown ((char*)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH); + version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major, + aname->minor, aname->build, aname->revision, + culture, pubtok); + g_free (pubtok); + + subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL); + g_free (name); + g_free (version); + g_free (culture); + + if (extra_gac_paths) { + paths = extra_gac_paths; + while (!result && *paths) { + fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", subpath, NULL); + result = mono_assembly_open_full (fullpath, status, refonly); + g_free (fullpath); + paths++; + } + } + + if (result) { + result->in_gac = TRUE; + g_free (subpath); + return result; + } + + fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), + "mono", "gac", subpath, NULL); + result = mono_assembly_open_full (fullpath, status, refonly); + g_free (fullpath); + + if (result) + result->in_gac = TRUE; + + g_free (subpath); + + return result; +} + +MonoAssembly* +mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *status) +{ + char *corlib_file; + MonoAssemblyName *aname; + + if (corlib) { + /* g_print ("corlib already loaded\n"); */ + return corlib; + } + + // In native client, Corlib is embedded in the executable as static variable corlibData +#if defined(__native_client__) + if (corlibData != NULL && corlibSize != 0) { + int status = 0; + /* First "FALSE" instructs mono not to make a copy. */ + /* Second "FALSE" says this is not just a ref. */ + MonoImage* image = mono_image_open_from_data_full (corlibData, corlibSize, FALSE, &status, FALSE); + if (image == NULL || status != 0) + g_print("mono_image_open_from_data_full failed: %d\n", status); + corlib = mono_assembly_load_from_full (image, "mscorlib", &status, FALSE); + if (corlib == NULL || status != 0) + g_print ("mono_assembly_load_from_full failed: %d\n", status); + if (corlib) + return corlib; + } +#endif + + // A nonstandard preload hook may provide a special mscorlib assembly + aname = mono_assembly_name_new ("mscorlib.dll"); + corlib = invoke_assembly_preload_hook (aname, assemblies_path); + mono_assembly_name_free (aname); + g_free (aname); + if (corlib != NULL) + goto return_corlib_and_facades; + + // This unusual directory layout can occur if mono is being built and run out of its own source repo + if (assemblies_path) { // Custom assemblies path set via MONO_PATH or mono_set_assemblies_path + corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status, FALSE); + if (corlib) + goto return_corlib_and_facades; + } + + /* Normal case: Load corlib from mono/ */ + corlib_file = g_build_filename ("mono", runtime->framework_version, "mscorlib.dll", NULL); + if (assemblies_path) { // Custom assemblies path + corlib = load_in_path (corlib_file, (const char**)assemblies_path, status, FALSE); + if (corlib) { + g_free (corlib_file); + goto return_corlib_and_facades; + } + } + corlib = load_in_path (corlib_file, default_path, status, FALSE); + g_free (corlib_file); + +return_corlib_and_facades: + if (corlib && !strcmp (runtime->framework_version, "4.5")) // FIXME: stop hardcoding 4.5 here + default_path [1] = g_strdup_printf ("%s/Facades", corlib->basedir); + + return corlib; +} + +MonoAssembly* +mono_assembly_load_full_nosearch (MonoAssemblyName *aname, + const char *basedir, + MonoImageOpenStatus *status, + gboolean refonly) +{ + MonoAssembly *result; + char *fullpath, *filename; + MonoAssemblyName maped_aname; + MonoAssemblyName maped_name_pp; + int ext_index; + const char *ext; + int len; + + aname = mono_assembly_remap_version (aname, &maped_aname); + + /* Reflection only assemblies don't get assembly binding */ + if (!refonly) + aname = mono_assembly_apply_binding (aname, &maped_name_pp); + + result = mono_assembly_loaded_full (aname, refonly); + if (result) + return result; + + result = refonly ? invoke_assembly_refonly_preload_hook (aname, assemblies_path) : invoke_assembly_preload_hook (aname, assemblies_path); + if (result) { + result->in_gac = FALSE; + return result; + } + + /* Currently we retrieve the loaded corlib for reflection + * only requests, like a common reflection only assembly + */ + if (strcmp (aname->name, "mscorlib") == 0 || strcmp (aname->name, "mscorlib.dll") == 0) { + return mono_assembly_load_corlib (mono_get_runtime_info (), status); + } + + len = strlen (aname->name); + for (ext_index = 0; ext_index < 2; ext_index ++) { + ext = ext_index == 0 ? ".dll" : ".exe"; + if (len > 4 && (!strcmp (aname->name + len - 4, ".dll") || !strcmp (aname->name + len - 4, ".exe"))) { + filename = g_strdup (aname->name); + /* Don't try appending .dll/.exe if it already has one of those extensions */ + ext_index++; + } else { + filename = g_strconcat (aname->name, ext, NULL); + } + + result = mono_assembly_load_from_gac (aname, filename, status, refonly); + if (result) { + g_free (filename); + return result; + } + + if (basedir) { + fullpath = g_build_filename (basedir, filename, NULL); + result = mono_assembly_open_full (fullpath, status, refonly); + g_free (fullpath); + if (result) { + result->in_gac = FALSE; + g_free (filename); + return result; + } + } + + result = load_in_path (filename, default_path, status, refonly); + if (result) + result->in_gac = FALSE; + g_free (filename); + if (result) + return result; + } + + return result; +} + +MonoAssembly* +mono_assembly_load_full_internal (MonoAssemblyName *aname, MonoAssembly *requesting, const char *basedir, MonoImageOpenStatus *status, gboolean refonly) +{ + MonoAssembly *result = mono_assembly_load_full_nosearch (aname, basedir, status, refonly); + + if (!result) + /* Try a postload search hook */ + result = mono_assembly_invoke_search_hook_internal (aname, requesting, refonly, TRUE); + return result; +} + +/** + * mono_assembly_load_full: + * @aname: A MonoAssemblyName with the assembly name to load. + * @basedir: A directory to look up the assembly at. + * @status: a pointer to a MonoImageOpenStatus to return the status of the load operation + * @refonly: Whether this assembly is being opened in "reflection-only" mode. + * + * Loads the assembly referenced by @aname, if the value of @basedir is not NULL, it + * attempts to load the assembly from that directory before probing the standard locations. + * + * If the assembly is being opened in reflection-only mode (@refonly set to TRUE) then no + * assembly binding takes place. + * + * Returns: the assembly referenced by @aname loaded or NULL on error. On error the + * value pointed by status is updated with an error code. + */ +MonoAssembly* +mono_assembly_load_full (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status, gboolean refonly) +{ + return mono_assembly_load_full_internal (aname, NULL, basedir, status, refonly); +} + +/** + * mono_assembly_load: + * @aname: A MonoAssemblyName with the assembly name to load. + * @basedir: A directory to look up the assembly at. + * @status: a pointer to a MonoImageOpenStatus to return the status of the load operation + * + * Loads the assembly referenced by @aname, if the value of @basedir is not NULL, it + * attempts to load the assembly from that directory before probing the standard locations. + * + * Returns: the assembly referenced by @aname loaded or NULL on error. On error the + * value pointed by status is updated with an error code. + */ +MonoAssembly* +mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status) +{ + return mono_assembly_load_full_internal (aname, NULL, basedir, status, FALSE); +} + +/** + * mono_assembly_loaded_full: + * @aname: an assembly to look for. + * @refonly: Whether this assembly is being opened in "reflection-only" mode. + * + * This is used to determine if the specified assembly has been loaded + * Returns: NULL If the given @aname assembly has not been loaded, or a pointer to + * a `MonoAssembly` that matches the `MonoAssemblyName` specified. + */ +MonoAssembly* +mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly) +{ + MonoAssembly *res; + MonoAssemblyName maped_aname; + + aname = mono_assembly_remap_version (aname, &maped_aname); + + res = mono_assembly_invoke_search_hook_internal (aname, NULL, refonly, FALSE); + + return res; +} + +/** + * mono_assembly_loaded: + * @aname: an assembly to look for. + * + * This is used to determine if the specified assembly has been loaded + + * Returns: NULL If the given @aname assembly has not been loaded, or a pointer to + * a `MonoAssembly` that matches the `MonoAssemblyName` specified. + */ +MonoAssembly* +mono_assembly_loaded (MonoAssemblyName *aname) +{ + return mono_assembly_loaded_full (aname, FALSE); +} + +void +mono_assembly_release_gc_roots (MonoAssembly *assembly) +{ + if (assembly == NULL || assembly == REFERENCE_MISSING) + return; + + if (assembly_is_dynamic (assembly)) { + int i; + MonoDynamicImage *dynimg = (MonoDynamicImage *)assembly->image; + for (i = 0; i < dynimg->image.module_count; ++i) + mono_dynamic_image_release_gc_roots ((MonoDynamicImage *)dynimg->image.modules [i]); + mono_dynamic_image_release_gc_roots (dynimg); + } +} + +/* + * Returns whether mono_assembly_close_finish() must be called as + * well. See comment for mono_image_close_except_pools() for why we + * unload in two steps. + */ +gboolean +mono_assembly_close_except_image_pools (MonoAssembly *assembly) +{ + GSList *tmp; + g_return_val_if_fail (assembly != NULL, FALSE); + + if (assembly == REFERENCE_MISSING) + return FALSE; + + /* Might be 0 already */ + if (InterlockedDecrement (&assembly->ref_count) > 0) + return FALSE; + + mono_profiler_assembly_event (assembly, MONO_PROFILE_START_UNLOAD); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Unloading assembly %s [%p].", assembly->aname.name, assembly); + + mono_debug_close_image (assembly->image); + + mono_assemblies_lock (); + loaded_assemblies = g_list_remove (loaded_assemblies, assembly); + mono_assemblies_unlock (); + + assembly->image->assembly = NULL; + + if (!mono_image_close_except_pools (assembly->image)) + assembly->image = NULL; + + for (tmp = assembly->friend_assembly_names; tmp; tmp = tmp->next) { + MonoAssemblyName *fname = (MonoAssemblyName *)tmp->data; + mono_assembly_name_free (fname); + g_free (fname); + } + g_slist_free (assembly->friend_assembly_names); + g_free (assembly->basedir); + + mono_profiler_assembly_event (assembly, MONO_PROFILE_END_UNLOAD); + + return TRUE; +} + +void +mono_assembly_close_finish (MonoAssembly *assembly) +{ + g_assert (assembly && assembly != REFERENCE_MISSING); + + if (assembly->image) + mono_image_close_finish (assembly->image); + + if (assembly_is_dynamic (assembly)) { + g_free ((char*)assembly->aname.culture); + } else { + g_free (assembly); + } +} + +/** + * mono_assembly_close: + * @assembly: the assembly to release. + * + * This method releases a reference to the @assembly. The assembly is + * only released when all the outstanding references to it are released. + */ +void +mono_assembly_close (MonoAssembly *assembly) +{ + if (mono_assembly_close_except_image_pools (assembly)) + mono_assembly_close_finish (assembly); +} + +MonoImage* +mono_assembly_load_module (MonoAssembly *assembly, guint32 idx) +{ + return mono_image_load_file_for_image (assembly->image, idx); +} + +/** + * mono_assembly_foreach: + * @func: function to invoke for each assembly loaded + * @user_data: data passed to the callback + * + * Invokes the provided @func callback for each assembly loaded into + * the runtime. The first parameter passed to the callback is the + * `MonoAssembly*`, and the second parameter is the @user_data. + * + * This is done for all assemblies loaded in the runtime, not just + * those loaded in the current application domain. + */ +void +mono_assembly_foreach (GFunc func, gpointer user_data) +{ + GList *copy; + + /* + * We make a copy of the list to avoid calling the callback inside the + * lock, which could lead to deadlocks. + */ + mono_assemblies_lock (); + copy = g_list_copy (loaded_assemblies); + mono_assemblies_unlock (); + + g_list_foreach (loaded_assemblies, func, user_data); + + g_list_free (copy); +} + +/** + * mono_assemblies_cleanup: + * + * Free all resources used by this module. + */ +void +mono_assemblies_cleanup (void) +{ + GSList *l; + + mono_os_mutex_destroy (&assemblies_mutex); + mono_os_mutex_destroy (&assembly_binding_mutex); + + for (l = loaded_assembly_bindings; l; l = l->next) { + MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data; + + mono_assembly_binding_info_free (info); + g_free (info); + } + g_slist_free (loaded_assembly_bindings); + + free_assembly_load_hooks (); + free_assembly_search_hooks (); + free_assembly_preload_hooks (); +} + +/*LOCKING takes the assembly_binding lock*/ +void +mono_assembly_cleanup_domain_bindings (guint32 domain_id) +{ + GSList **iter; + + mono_assembly_binding_lock (); + iter = &loaded_assembly_bindings; + while (*iter) { + GSList *l = *iter; + MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data; + + if (info->domain_id == domain_id) { + *iter = l->next; + mono_assembly_binding_info_free (info); + g_free (info); + g_slist_free_1 (l); + } else { + iter = &l->next; + } + } + mono_assembly_binding_unlock (); +} + +/* + * Holds the assembly of the application, for + * System.Diagnostics.Process::MainModule + */ +static MonoAssembly *main_assembly=NULL; + +void +mono_assembly_set_main (MonoAssembly *assembly) +{ + main_assembly = assembly; +} + +/** + * mono_assembly_get_main: + * + * Returns: the assembly for the application, the first assembly that is loaded by the VM + */ +MonoAssembly * +mono_assembly_get_main (void) +{ + return (main_assembly); +} + +/** + * mono_assembly_get_image: + * @assembly: The assembly to retrieve the image from + * + * Returns: the MonoImage associated with this assembly. + */ +MonoImage* +mono_assembly_get_image (MonoAssembly *assembly) +{ + return assembly->image; +} + +/** + * mono_assembly_get_name: + * @assembly: The assembly to retrieve the name from + * + * The returned name's lifetime is the same as @assembly's. + * + * Returns: the MonoAssemblyName associated with this assembly. + */ +MonoAssemblyName * +mono_assembly_get_name (MonoAssembly *assembly) +{ + return &assembly->aname; +} + +void +mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies) +{ + bundles = assemblies; +} + +#define MONO_DECLSEC_FORMAT_10 0x3C +#define MONO_DECLSEC_FORMAT_20 0x2E +#define MONO_DECLSEC_FIELD 0x53 +#define MONO_DECLSEC_PROPERTY 0x54 + +#define SKIP_VISIBILITY_XML_ATTRIBUTE ("\"SkipVerification\"") +#define SKIP_VISIBILITY_ATTRIBUTE_NAME ("System.Security.Permissions.SecurityPermissionAttribute") +#define SKIP_VISIBILITY_ATTRIBUTE_SIZE (sizeof (SKIP_VISIBILITY_ATTRIBUTE_NAME) - 1) +#define SKIP_VISIBILITY_PROPERTY_NAME ("SkipVerification") +#define SKIP_VISIBILITY_PROPERTY_SIZE (sizeof (SKIP_VISIBILITY_PROPERTY_NAME) - 1) + +static gboolean +mono_assembly_try_decode_skip_verification_param (const char *p, const char **resp, gboolean *abort_decoding) +{ + int len; + switch (*p++) { + case MONO_DECLSEC_PROPERTY: + break; + case MONO_DECLSEC_FIELD: + default: + *abort_decoding = TRUE; + return FALSE; + break; + } + + if (*p++ != MONO_TYPE_BOOLEAN) { + *abort_decoding = TRUE; + return FALSE; + } + + /* property name length */ + len = mono_metadata_decode_value (p, &p); + + if (len >= SKIP_VISIBILITY_PROPERTY_SIZE && !memcmp (p, SKIP_VISIBILITY_PROPERTY_NAME, SKIP_VISIBILITY_PROPERTY_SIZE)) { + p += len; + return *p; + } + p += len + 1; + + *resp = p; + return FALSE; +} + +static gboolean +mono_assembly_try_decode_skip_verification (const char *p, const char *endn) +{ + int i, j, num, len, params_len; + + if (*p == MONO_DECLSEC_FORMAT_10) { + gsize read, written; + char *res = g_convert (p, endn - p, "UTF-8", "UTF-16LE", &read, &written, NULL); + if (res) { + gboolean found = strstr (res, SKIP_VISIBILITY_XML_ATTRIBUTE) != NULL; + g_free (res); + return found; + } + return FALSE; + } + if (*p++ != MONO_DECLSEC_FORMAT_20) + return FALSE; + + /* number of encoded permission attributes */ + num = mono_metadata_decode_value (p, &p); + for (i = 0; i < num; ++i) { + gboolean is_valid = FALSE; + gboolean abort_decoding = FALSE; + + /* attribute name length */ + len = mono_metadata_decode_value (p, &p); + + /* We don't really need to fully decode the type. Comparing the name is enough */ + is_valid = len >= SKIP_VISIBILITY_ATTRIBUTE_SIZE && !memcmp (p, SKIP_VISIBILITY_ATTRIBUTE_NAME, SKIP_VISIBILITY_ATTRIBUTE_SIZE); + + p += len; + + /*size of the params table*/ + params_len = mono_metadata_decode_value (p, &p); + if (is_valid) { + const char *params_end = p + params_len; + + /* number of parameters */ + len = mono_metadata_decode_value (p, &p); + + for (j = 0; j < len; ++j) { + if (mono_assembly_try_decode_skip_verification_param (p, &p, &abort_decoding)) + return TRUE; + if (abort_decoding) + break; + } + p = params_end; + } else { + p += params_len; + } + } + + return FALSE; +} + + +gboolean +mono_assembly_has_skip_verification (MonoAssembly *assembly) +{ + MonoTableInfo *t; + guint32 cols [MONO_DECL_SECURITY_SIZE]; + const char *blob; + int i, len; + + if (MONO_SECMAN_FLAG_INIT (assembly->skipverification)) + return MONO_SECMAN_FLAG_GET_VALUE (assembly->skipverification); + + t = &assembly->image->tables [MONO_TABLE_DECLSECURITY]; + + for (i = 0; i < t->rows; ++i) { + mono_metadata_decode_row (t, i, cols, MONO_DECL_SECURITY_SIZE); + if ((cols [MONO_DECL_SECURITY_PARENT] & MONO_HAS_DECL_SECURITY_MASK) != MONO_HAS_DECL_SECURITY_ASSEMBLY) + continue; + if (cols [MONO_DECL_SECURITY_ACTION] != SECURITY_ACTION_REQMIN) + continue; + + blob = mono_metadata_blob_heap (assembly->image, cols [MONO_DECL_SECURITY_PERMISSIONSET]); + len = mono_metadata_decode_blob_size (blob, &blob); + if (!len) + continue; + + if (mono_assembly_try_decode_skip_verification (blob, blob + len)) { + MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, TRUE); + return TRUE; + } + } + + MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, FALSE); + return FALSE; +} diff --git a/mono/metadata/assembly.c.REMOVED.git-id b/mono/metadata/assembly.c.REMOVED.git-id deleted file mode 100644 index ae1e118c37..0000000000 --- a/mono/metadata/assembly.c.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -7bd02ff37eea77a111cb30ce2d3b64953d487736 \ No newline at end of file diff --git a/mono/metadata/class.c.REMOVED.git-id b/mono/metadata/class.c.REMOVED.git-id index fe47ef994f..aeea9c5019 100644 --- a/mono/metadata/class.c.REMOVED.git-id +++ b/mono/metadata/class.c.REMOVED.git-id @@ -1 +1 @@ -56e12ad673e2765956a4fc89dade62a15656a02f \ No newline at end of file +17da478b5e9c1f089d76de0bc275da6ab5e21077 \ No newline at end of file diff --git a/mono/metadata/domain-internals.h b/mono/metadata/domain-internals.h index 5a4269b6d8..a3989ab74a 100644 --- a/mono/metadata/domain-internals.h +++ b/mono/metadata/domain-internals.h @@ -697,8 +697,4 @@ mono_runtime_init_checked (MonoDomain *domain, MonoThreadStartCB start_cb, MonoT void mono_context_init_checked (MonoDomain *domain, MonoError *error); -gboolean -mono_assembly_get_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error); - - #endif /* __MONO_METADATA_DOMAIN_INTERNALS_H__ */ diff --git a/mono/metadata/domain.c b/mono/metadata/domain.c index 93aced664f..a3ba4a76e0 100644 --- a/mono/metadata/domain.c +++ b/mono/metadata/domain.c @@ -807,16 +807,6 @@ mono_init_internal (const char *filename, const char *exe_filename, const char * mono_profiler_appdomain_name (domain, domain->friendly_name); - /* Have to do this quite late so that we at least have System.Object */ - MonoError custom_attr_error; - if (mono_assembly_get_reference_assembly_attribute (ass, &custom_attr_error)) { - char *corlib_file = g_build_filename (mono_assembly_getrootdir (), "mono", current_runtime->framework_version, "mscorlib.dll", NULL); - g_print ("Could not load file or assembly %s. Reference assemblies should not be loaded for execution. They can only be loaded in the Reflection-only loader context.", corlib_file); - g_free (corlib_file); - exit (1); - } - mono_error_assert_ok (&custom_attr_error); - return domain; } diff --git a/mono/mini/Makefile.am b/mono/mini/Makefile.am index cddc2dfbe6..2833af7157 100644 --- a/mono/mini/Makefile.am +++ b/mono/mini/Makefile.am @@ -824,7 +824,7 @@ EXTRA_DIST = TestDriver.cs \ Makefile.am.in version.h: Makefile - echo "#define FULL_VERSION \"Stable 4.6.0.245/746756c\"" > version.h + echo "#define FULL_VERSION \"Stable 4.6.1.3/abb06f1\"" > version.h # Utility target for patching libtool to speed up linking patch-libtool: diff --git a/mono/mini/Makefile.am.in b/mono/mini/Makefile.am.in index cddc2dfbe6..2833af7157 100755 --- a/mono/mini/Makefile.am.in +++ b/mono/mini/Makefile.am.in @@ -824,7 +824,7 @@ EXTRA_DIST = TestDriver.cs \ Makefile.am.in version.h: Makefile - echo "#define FULL_VERSION \"Stable 4.6.0.245/746756c\"" > version.h + echo "#define FULL_VERSION \"Stable 4.6.1.3/abb06f1\"" > version.h # Utility target for patching libtool to speed up linking patch-libtool: diff --git a/mono/mini/Makefile.in.REMOVED.git-id b/mono/mini/Makefile.in.REMOVED.git-id index 24b9d6a55a..bd6917fbf9 100644 --- a/mono/mini/Makefile.in.REMOVED.git-id +++ b/mono/mini/Makefile.in.REMOVED.git-id @@ -1 +1 @@ -d4bf30694cd360d7152e64c43a9e4838e71f2c0c \ No newline at end of file +906197df9f69b49f888b78629651ab70ec983eaf \ No newline at end of file diff --git a/mono/mini/mini-arm64.c.REMOVED.git-id b/mono/mini/mini-arm64.c.REMOVED.git-id index b9af9a8cf9..8d62e892e3 100644 --- a/mono/mini/mini-arm64.c.REMOVED.git-id +++ b/mono/mini/mini-arm64.c.REMOVED.git-id @@ -1 +1 @@ -688594d96602b50770485925c92a521726edeea7 \ No newline at end of file +995aea83d98094d62e65b814a85ba53c4744c5c1 \ No newline at end of file diff --git a/mono/mini/mini-llvm.c.REMOVED.git-id b/mono/mini/mini-llvm.c.REMOVED.git-id index f5f4db2ac7..3af216e208 100644 --- a/mono/mini/mini-llvm.c.REMOVED.git-id +++ b/mono/mini/mini-llvm.c.REMOVED.git-id @@ -1 +1 @@ -1bf1ba6fa13f613a176781c0d4a9c16183273460 \ No newline at end of file +33a4ca8d181b6ff7032ee475b3c355317dda43da \ No newline at end of file diff --git a/mono/mini/version.h b/mono/mini/version.h index a0c31789a6..c953416145 100644 --- a/mono/mini/version.h +++ b/mono/mini/version.h @@ -1 +1 @@ -#define FULL_VERSION "Stable 4.6.0.245/746756c" +#define FULL_VERSION "Stable 4.6.1.3/abb06f1" diff --git a/mono/tests/Makefile.am b/mono/tests/Makefile.am index f0f7def44b..cf5eada2f4 100644 --- a/mono/tests/Makefile.am +++ b/mono/tests/Makefile.am @@ -436,8 +436,7 @@ BASE_TEST_CS_SRC= \ pinvoke_ppci.cs \ pinvoke_ppcf.cs \ pinvoke_ppcd.cs \ - bug-29585.cs \ - reference-loader.cs + bug-29585.cs TEST_CS_SRC_DIST= \ $(BASE_TEST_CS_SRC) \ @@ -701,13 +700,11 @@ TEST_IL_SRC= \ # but that need to be compiled PREREQ_IL_SRC=event-il.il module-cctor.il PREREQ_CS_SRC= -PREREQ_IL_DLL_SRC= -PREREQ_CS_DLL_SRC=TestingReferenceAssembly.cs TestingReferenceReferenceAssembly.cs +PREREQ_IL_DLL_SRC=event-il.il module-cctor.il +PREREQ_CS_DLL_SRC= -PREREQSI_IL=$(PREREQ_IL_SRC:.il=.exe) \ - $(PREREQ_IL_DLL_SRC:.il=.dll) -PREREQSI_CS=$(PREREQ_CS_SRC:.cs=.exe) \ - $(PREREQ_CS_DLL_SRC:.cs=.dll) +PREREQSI_IL=$(PREREQ_IL_SRC:.il=.exe) +PREREQSI_CS=$(PREREQ_CS_SRC:.cs=.exe) TESTSI_CS=$(TEST_CS_SRC:.cs=.exe) TESTSI_IL=$(TEST_IL_SRC:.il=.exe) TESTBS=$(BENCHSRC:.cs=.exe) @@ -722,13 +719,6 @@ EXTRA_DIST=test-driver test-runner.cs $(TEST_CS_SRC_DIST) $(TEST_IL_SRC) \ %.exe: %.cs TestDriver.dll $(MCS) -r:System.dll -r:System.Xml.dll -r:System.Core.dll -r:TestDriver.dll -r:Mono.Posix.dll -out:$@ $< -%.dll: %.cs - $(MCS) -r:System.dll -target:library -out:$@ $< - -TestingReferenceReferenceAssembly.dll: TestingReferenceReferenceAssembly.cs TestingReferenceAssembly.dll - $(MCS) -r:TestingReferenceAssembly.dll -target:library -out:$@ $< - - # mkbundle works on ppc, but the pkg-config POC doesn't when run with make test if POWERPC test_platform: diff --git a/mono/tests/Makefile.in b/mono/tests/Makefile.in index bd17f8e130..62dba2054e 100644 --- a/mono/tests/Makefile.in +++ b/mono/tests/Makefile.in @@ -842,8 +842,7 @@ BASE_TEST_CS_SRC = \ pinvoke_ppci.cs \ pinvoke_ppcf.cs \ pinvoke_ppcd.cs \ - bug-29585.cs \ - reference-loader.cs + bug-29585.cs TEST_CS_SRC_DIST = \ $(BASE_TEST_CS_SRC) \ @@ -1057,14 +1056,10 @@ TEST_IL_SRC = \ # but that need to be compiled PREREQ_IL_SRC = event-il.il module-cctor.il PREREQ_CS_SRC = -PREREQ_IL_DLL_SRC = -PREREQ_CS_DLL_SRC = TestingReferenceAssembly.cs TestingReferenceReferenceAssembly.cs -PREREQSI_IL = $(PREREQ_IL_SRC:.il=.exe) \ - $(PREREQ_IL_DLL_SRC:.il=.dll) - -PREREQSI_CS = $(PREREQ_CS_SRC:.cs=.exe) \ - $(PREREQ_CS_DLL_SRC:.cs=.dll) - +PREREQ_IL_DLL_SRC = event-il.il module-cctor.il +PREREQ_CS_DLL_SRC = +PREREQSI_IL = $(PREREQ_IL_SRC:.il=.exe) +PREREQSI_CS = $(PREREQ_CS_SRC:.cs=.exe) TESTSI_CS = $(TEST_CS_SRC:.cs=.exe) TESTSI_IL = $(TEST_IL_SRC:.il=.exe) TESTBS = $(BENCHSRC:.cs=.exe) @@ -1601,12 +1596,6 @@ aotcheck: testaot gshared-aot %.exe: %.cs TestDriver.dll $(MCS) -r:System.dll -r:System.Xml.dll -r:System.Core.dll -r:TestDriver.dll -r:Mono.Posix.dll -out:$@ $< -%.dll: %.cs - $(MCS) -r:System.dll -target:library -out:$@ $< - -TestingReferenceReferenceAssembly.dll: TestingReferenceReferenceAssembly.cs TestingReferenceAssembly.dll - $(MCS) -r:TestingReferenceAssembly.dll -target:library -out:$@ $< - # mkbundle works on ppc, but the pkg-config POC doesn't when run with make test @POWERPC_TRUE@test_platform: # Can't use mkbundle on win32 since there is no static build there diff --git a/mono/tests/reference-loader.cs b/mono/tests/reference-loader.cs deleted file mode 100644 index 9cdda7a719..0000000000 --- a/mono/tests/reference-loader.cs +++ /dev/null @@ -1,111 +0,0 @@ -// -// reference-loader.cs: -// -// Test for reference assembly loading - -using System; -using System.IO; -using System.Reflection; - -public class Tests { - public static int Main (string[] args) - { - return TestDriver.RunTests (typeof (Tests), args); - } - - public static int test_0_loadFrom_reference () - { - // Check that loading a reference assembly by filename for execution is an error - try { - var a = Assembly.LoadFrom ("./TestingReferenceAssembly.dll"); - } catch (BadImageFormatException exn) { - // Console.Error.WriteLine ("exn was {0}", exn); - return 0; - } - return 1; - } - - public static int test_0_load_reference () - { - // Check that loading a reference assembly for execution is an error - try { - var an = new AssemblyName ("TestingReferenceAssembly"); - var a = Assembly.Load (an); - } catch (BadImageFormatException exn) { - //Console.Error.WriteLine ("exn was {0}", exn); - return 0; - } catch (FileNotFoundException exn) { - Console.Error.WriteLine ("incorrect exn was {0}", exn); - return 2; - } - return 1; - } - - public static int test_0_reflection_load_reference () - { - // Check that reflection-only loading a reference assembly is okay - var an = new AssemblyName ("TestingReferenceAssembly"); - var a = Assembly.ReflectionOnlyLoad (an.FullName); - var t = a.GetType ("X"); - var f = t.GetField ("Y"); - if (f.FieldType.Equals (typeof (Int32))) - return 0; - return 1; - } - - public static int test_0_load_reference_asm_via_reference () - { - // Check that loading an assembly that references a reference assembly doesn't succeed. - var an = new AssemblyName ("TestingReferenceReferenceAssembly"); - try { - var a = Assembly.Load (an); - var t = a.GetType ("Z"); - } catch (FileNotFoundException){ - return 0; - } - return 1; - } - - public static int test_0_reflection_load_reference_asm_via_reference () - { - // Check that reflection-only loading an assembly that - // references a reference assembly is okay. - var an = new AssemblyName ("TestingReferenceReferenceAssembly"); - var a = Assembly.ReflectionOnlyLoad (an.FullName); - var t = a.GetType ("Z"); - var f = t.GetField ("Y"); - if (f.FieldType.Equals (typeof (Int32))) - return 0; - return 1; - } - - - public static int test_0_load_reference_bytes () - { - // Check that loading a reference assembly from a byte array for execution is an error - byte[] bs = File.ReadAllBytes ("./TestingReferenceAssembly.dll"); - try { - var a = Assembly.Load (bs); - } catch (BadImageFormatException) { - return 0; - } catch (FileNotFoundException exn) { - Console.Error.WriteLine ("incorrect exn was {0}", exn); - return 2; - } - return 1; - } - - public static int test_0_reflection_load_reference_bytes () - { - // Check that loading a reference assembly from a byte - // array for reflection only is okay. - byte[] bs = File.ReadAllBytes ("./TestingReferenceAssembly.dll"); - var a = Assembly.ReflectionOnlyLoad (bs); - var t = a.GetType ("X"); - var f = t.GetField ("Y"); - if (f.FieldType.Equals (typeof (Int32))) - return 0; - return 1; - } - -} diff --git a/po/mcs/de.gmo b/po/mcs/de.gmo index c36f8cf8bb1c22f74774add046534815f76adc34..34d82564c07ff0966797a5ecbe5594eaefb13526 100644 GIT binary patch delta 21 ccmbQIHBW296D|%T3k5?%D-(mwZ@7%O08l#yrT_o{ delta 21 ccmbQIHBW296D|%z69ofHD?`i8Z@7%O08m{9t^fc4 diff --git a/po/mcs/de.po.REMOVED.git-id b/po/mcs/de.po.REMOVED.git-id index f29ee6565a..f3df2c0c14 100644 --- a/po/mcs/de.po.REMOVED.git-id +++ b/po/mcs/de.po.REMOVED.git-id @@ -1 +1 @@ -56ea4b55d1668de5427cf107eaeb0953c60c28cc \ No newline at end of file +8440bded44bc0320b80d941464dc88082c9fc152 \ No newline at end of file diff --git a/po/mcs/es.gmo b/po/mcs/es.gmo index c4210ff5026ad5a8118962871e3c953ff25f32d9..2c3362ad063c40dfe28ad9344dba4be921257f15 100644 GIT binary patch delta 21 ccmX?Ef3kjqt|Et#g@U1>m5IS-Q^hHA09U&P+yDRo delta 21 ccmX?Ef3kjqt|Et_iGqQpm7(QkQ^hHA09V}x\n" "Language-Team: LANGUAGE \n" diff --git a/po/mcs/pt_BR.gmo b/po/mcs/pt_BR.gmo index 1426ae9b4e7d07d36e9ccc0e6676aef056389fe5..457b5346c1c80bd360c2d330216971ef81d0622c 100644 GIT binary patch delta 23 fcmX@Po8{zgmJO2AIE*Y53=OSJ3^prHThR^xZ3qaP delta 23 fcmX@Po8{zgmJO2AI1Eh`3@oh-EjKGpThR^xZ7K+( diff --git a/po/mcs/pt_BR.po.REMOVED.git-id b/po/mcs/pt_BR.po.REMOVED.git-id index 7bdabd08d9..e0de9b8197 100644 --- a/po/mcs/pt_BR.po.REMOVED.git-id +++ b/po/mcs/pt_BR.po.REMOVED.git-id @@ -1 +1 @@ -9098f30cedafc6c19c09061033e3210b47cd21ec \ No newline at end of file +740c11c139617ffbd21ab7a6a39ae82f22019014 \ No newline at end of file