[Mono.Runtime.Tests] Exclude simd tests
[mono-project.git] / mono / metadata / assembly.c
blob693732743a16357adf22d28377717f476332bbfd
1 /**
2 * \file
3 * Routines for loading assemblies.
4 *
5 * Author:
6 * Miguel de Icaza (miguel@ximian.com)
8 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
9 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
10 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include <config.h>
14 #include <stdio.h>
15 #include <glib.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include "assembly.h"
20 #include "assembly-internals.h"
21 #include "image.h"
22 #include "image-internals.h"
23 #include "object-internals.h"
24 #include <mono/metadata/loader.h>
25 #include <mono/metadata/tabledefs.h>
26 #include <mono/metadata/custom-attrs-internals.h>
27 #include <mono/metadata/metadata-internals.h>
28 #include <mono/metadata/profiler-private.h>
29 #include <mono/metadata/class-internals.h>
30 #include <mono/metadata/domain-internals.h>
31 #include <mono/metadata/reflection-internals.h>
32 #include <mono/metadata/mono-endian.h>
33 #include <mono/metadata/mono-debug.h>
34 #include <mono/utils/mono-uri.h>
35 #include <mono/metadata/mono-config.h>
36 #include <mono/metadata/mono-config-dirs.h>
37 #include <mono/utils/mono-digest.h>
38 #include <mono/utils/mono-logger-internals.h>
39 #include <mono/utils/mono-path.h>
40 #include <mono/metadata/reflection.h>
41 #include <mono/metadata/coree.h>
42 #include <mono/metadata/cil-coff.h>
43 #include <mono/utils/mono-io-portability.h>
44 #include <mono/utils/atomic.h>
45 #include <mono/utils/mono-os-mutex.h>
47 #ifndef HOST_WIN32
48 #include <sys/types.h>
49 #include <unistd.h>
50 #include <sys/stat.h>
51 #endif
53 #ifdef HOST_DARWIN
54 #include <mach-o/dyld.h>
55 #endif
57 /* 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 */
58 typedef struct {
59 const char* assembly_name;
60 guint8 version_set_index;
61 const char* new_assembly_name;
62 gboolean only_lower_versions;
63 gboolean framework_facade_assembly;
64 } AssemblyVersionMap;
66 /* the default search path is empty, the first slot is replaced with the computed value */
67 static char*
68 default_path [] = {
69 NULL,
70 NULL,
71 NULL
74 /* Contains the list of directories to be searched for assemblies (MONO_PATH) */
75 static char **assemblies_path = NULL;
77 /* Contains the list of directories that point to auxiliary GACs */
78 static char **extra_gac_paths = NULL;
80 #ifndef DISABLE_DESKTOP_LOADER
82 #define FACADE_ASSEMBLY(str) {str, 0, NULL, FALSE, TRUE}
84 static GHashTable* assembly_remapping_table;
85 /* The list of system assemblies what will be remapped to the running
86 * runtime version.
87 * This list is stored in @assembly_remapping_table during initialization.
88 * Keep it sorted just to make maintenance easier.
90 * The integer number is an index in the MonoRuntimeInfo structure, whose
91 * values can be found in domain.c - supported_runtimes. Look there
92 * to understand what remapping will be made.
94 * .NET version can be found at https://github.com/dotnet/coreclr/blob/master/src/inc/fxretarget.h#L99
97 static const AssemblyVersionMap framework_assemblies [] = {
98 {"Accessibility", 0},
99 {"Commons.Xml.Relaxng", 0},
100 {"CustomMarshalers", 0},
101 {"I18N", 0},
102 {"I18N.CJK", 0},
103 {"I18N.MidEast", 0},
104 {"I18N.Other", 0},
105 {"I18N.Rare", 0},
106 {"I18N.West", 0},
107 {"Microsoft.Build.Engine", 2, NULL, TRUE},
108 {"Microsoft.Build.Framework", 2, NULL, TRUE},
109 {"Microsoft.Build.Tasks", 2, "Microsoft.Build.Tasks.v4.0"},
110 {"Microsoft.Build.Tasks.v3.5", 2, "Microsoft.Build.Tasks.v4.0"},
111 {"Microsoft.Build.Utilities", 2, "Microsoft.Build.Utilities.v4.0"},
112 {"Microsoft.Build.Utilities.v3.5", 2, "Microsoft.Build.Utilities.v4.0"},
113 {"Microsoft.CSharp", 0},
114 {"Microsoft.VisualBasic", 1},
115 {"Microsoft.VisualC", 1},
116 FACADE_ASSEMBLY ("Microsoft.Win32.Primitives"),
117 FACADE_ASSEMBLY ("Microsoft.Win32.Registry"),
118 FACADE_ASSEMBLY ("Microsoft.Win32.Registry.AccessControl"),
119 {"Mono.Cairo", 0},
120 {"Mono.CompilerServices.SymbolWriter", 0},
121 {"Mono.Data", 0},
122 {"Mono.Data.SybaseClient", 0},
123 {"Mono.Data.Tds", 0},
124 {"Mono.Data.TdsClient", 0},
125 {"Mono.GetOptions", 0},
126 {"Mono.Http", 0},
127 {"Mono.Posix", 0},
128 {"Mono.Security", 0},
129 {"Mono.Security.Win32", 0},
130 {"Mono.Xml.Ext", 0},
131 {"Novell.Directory.Ldap", 0},
132 {"PEAPI", 0},
133 {"System", 0},
134 FACADE_ASSEMBLY ("System.AppContext"),
135 FACADE_ASSEMBLY ("System.Buffers"),
136 FACADE_ASSEMBLY ("System.Collections"),
137 FACADE_ASSEMBLY ("System.Collections.Concurrent"),
138 FACADE_ASSEMBLY ("System.Collections.NonGeneric"),
139 FACADE_ASSEMBLY ("System.Collections.Specialized"),
140 FACADE_ASSEMBLY ("System.ComponentModel"),
141 FACADE_ASSEMBLY ("System.ComponentModel.Annotations"),
142 {"System.ComponentModel.Composition", 2},
143 {"System.ComponentModel.DataAnnotations", 2},
144 FACADE_ASSEMBLY ("System.ComponentModel.EventBasedAsync"),
145 FACADE_ASSEMBLY ("System.ComponentModel.Primitives"),
146 FACADE_ASSEMBLY ("System.ComponentModel.TypeConverter"),
147 {"System.Configuration", 0},
148 {"System.Configuration.Install", 0},
149 FACADE_ASSEMBLY ("System.Console"),
150 {"System.Core", 2},
151 {"System.Data", 0},
152 FACADE_ASSEMBLY ("System.Data.Common"),
153 {"System.Data.DataSetExtensions", 0},
154 {"System.Data.Entity", 0},
155 {"System.Data.Linq", 2},
156 {"System.Data.OracleClient", 0},
157 {"System.Data.Services", 2},
158 {"System.Data.Services.Client", 2},
159 FACADE_ASSEMBLY ("System.Data.SqlClient"),
160 {"System.Data.SqlXml", 0},
161 {"System.Deployment", 0},
162 {"System.Design", 0},
163 FACADE_ASSEMBLY ("System.Diagnostics.Contracts"),
164 FACADE_ASSEMBLY ("System.Diagnostics.Debug"),
165 FACADE_ASSEMBLY ("System.Diagnostics.FileVersionInfo"),
166 FACADE_ASSEMBLY ("System.Diagnostics.Process"),
167 FACADE_ASSEMBLY ("System.Diagnostics.StackTrace"),
168 FACADE_ASSEMBLY ("System.Diagnostics.TextWriterTraceListener"),
169 FACADE_ASSEMBLY ("System.Diagnostics.Tools"),
170 FACADE_ASSEMBLY ("System.Diagnostics.TraceEvent"),
171 FACADE_ASSEMBLY ("System.Diagnostics.TraceSource"),
172 FACADE_ASSEMBLY ("System.Diagnostics.Tracing"),
173 {"System.DirectoryServices", 0},
174 {"System.DirectoryServices.Protocols", 0},
175 {"System.Drawing", 0},
176 FACADE_ASSEMBLY ("System.Drawing.Common"),
177 {"System.Drawing.Design", 0},
178 FACADE_ASSEMBLY ("System.Drawing.Primitives"),
179 {"System.Dynamic", 0},
180 FACADE_ASSEMBLY ("System.Dynamic.Runtime"),
181 {"System.EnterpriseServices", 0},
182 FACADE_ASSEMBLY ("System.Globalization"),
183 FACADE_ASSEMBLY ("System.Globalization.Calendars"),
184 FACADE_ASSEMBLY ("System.Globalization.Extensions"),
185 {"System.IdentityModel", 3},
186 {"System.IdentityModel.Selectors", 3},
187 FACADE_ASSEMBLY ("System.IO"),
188 {"System.IO.Compression", 2},
189 {"System.IO.Compression.FileSystem", 0},
190 FACADE_ASSEMBLY ("System.IO.Compression.ZipFile"),
191 FACADE_ASSEMBLY ("System.IO.FileSystem"),
192 FACADE_ASSEMBLY ("System.IO.FileSystem.AccessControl"),
193 FACADE_ASSEMBLY ("System.IO.FileSystem.DriveInfo"),
194 FACADE_ASSEMBLY ("System.IO.FileSystem.Primitives"),
195 FACADE_ASSEMBLY ("System.IO.FileSystem.Watcher"),
196 FACADE_ASSEMBLY ("System.IO.IsolatedStorage"),
197 FACADE_ASSEMBLY ("System.IO.MemoryMappedFiles"),
198 FACADE_ASSEMBLY ("System.IO.Packaging"),
199 FACADE_ASSEMBLY ("System.IO.Pipes"),
200 FACADE_ASSEMBLY ("System.IO.UnmanagedMemoryStream"),
201 FACADE_ASSEMBLY ("System.Linq"),
202 FACADE_ASSEMBLY ("System.Linq.Expressions"),
203 FACADE_ASSEMBLY ("System.Linq.Parallel"),
204 FACADE_ASSEMBLY ("System.Linq.Queryable"),
205 {"System.Management", 0},
206 FACADE_ASSEMBLY ("System.Memory"),
207 {"System.Messaging", 0},
208 {"System.Net", 2},
209 FACADE_ASSEMBLY ("System.Net.AuthenticationManager"),
210 FACADE_ASSEMBLY ("System.Net.Cache"),
211 {"System.Net.Http", 4},
212 {"System.Net.Http.Rtc", 0},
213 {"System.Net.Http.WebRequest", 0},
214 FACADE_ASSEMBLY ("System.Net.HttpListener"),
215 FACADE_ASSEMBLY ("System.Net.Mail"),
216 FACADE_ASSEMBLY ("System.Net.NameResolution"),
217 FACADE_ASSEMBLY ("System.Net.NetworkInformation"),
218 FACADE_ASSEMBLY ("System.Net.Ping"),
219 FACADE_ASSEMBLY ("System.Net.Primitives"),
220 FACADE_ASSEMBLY ("System.Net.Requests"),
221 FACADE_ASSEMBLY ("System.Net.Security"),
222 FACADE_ASSEMBLY ("System.Net.ServicePoint"),
223 FACADE_ASSEMBLY ("System.Net.Sockets"),
224 FACADE_ASSEMBLY ("System.Net.Utilities"),
225 FACADE_ASSEMBLY ("System.Net.WebHeaderCollection"),
226 FACADE_ASSEMBLY ("System.Net.WebSockets"),
227 FACADE_ASSEMBLY ("System.Net.WebSockets.Client"),
228 {"System.Numerics", 3},
229 {"System.Numerics.Vectors", 3},
230 FACADE_ASSEMBLY ("System.ObjectModel"),
231 FACADE_ASSEMBLY ("System.Reflection"),
232 {"System.Reflection.Context", 0},
233 FACADE_ASSEMBLY ("System.Reflection.DispatchProxy"),
234 FACADE_ASSEMBLY ("System.Reflection.Emit"),
235 FACADE_ASSEMBLY ("System.Reflection.Emit.ILGeneration"),
236 FACADE_ASSEMBLY ("System.Reflection.Emit.Lightweight"),
237 FACADE_ASSEMBLY ("System.Reflection.Extensions"),
238 FACADE_ASSEMBLY ("System.Reflection.Primitives"),
239 FACADE_ASSEMBLY ("System.Reflection.TypeExtensions"),
240 FACADE_ASSEMBLY ("System.Resources.Reader"),
241 FACADE_ASSEMBLY ("System.Resources.ReaderWriter"),
242 FACADE_ASSEMBLY ("System.Resources.ResourceManager"),
243 FACADE_ASSEMBLY ("System.Resources.Writer"),
244 FACADE_ASSEMBLY ("System.Runtime"),
245 {"System.Runtime.Caching", 0},
246 FACADE_ASSEMBLY ("System.Runtime.CompilerServices.VisualC"),
247 {"System.Runtime.DurableInstancing", 0},
248 FACADE_ASSEMBLY ("System.Runtime.Extensions"),
249 FACADE_ASSEMBLY ("System.Runtime.Handles"),
250 FACADE_ASSEMBLY ("System.Runtime.InteropServices"),
251 FACADE_ASSEMBLY ("System.Runtime.InteropServices.RuntimeInformation"),
252 FACADE_ASSEMBLY ("System.Runtime.InteropServices.WindowsRuntime"),
253 FACADE_ASSEMBLY ("System.Runtime.Loader"),
254 FACADE_ASSEMBLY ("System.Runtime.Numerics"),
255 {"System.Runtime.Remoting", 0},
256 {"System.Runtime.Serialization", 3},
257 FACADE_ASSEMBLY ("System.Runtime.Serialization.Formatters"),
258 {"System.Runtime.Serialization.Formatters.Soap", 0},
259 FACADE_ASSEMBLY ("System.Runtime.Serialization.Json"),
260 FACADE_ASSEMBLY ("System.Runtime.Serialization.Primitives"),
261 FACADE_ASSEMBLY ("System.Runtime.Serialization.Xml"),
262 {"System.Security", 0},
263 FACADE_ASSEMBLY ("System.Security.AccessControl"),
264 FACADE_ASSEMBLY ("System.Security.Claims"),
265 FACADE_ASSEMBLY ("System.Security.Cryptography.Algorithms"),
266 FACADE_ASSEMBLY ("System.Security.Cryptography.Cng"),
267 FACADE_ASSEMBLY ("System.Security.Cryptography.Csp"),
268 FACADE_ASSEMBLY ("System.Security.Cryptography.DeriveBytes"),
269 FACADE_ASSEMBLY ("System.Security.Cryptography.Encoding"),
270 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption"),
271 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.Aes"),
272 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.ECDiffieHellman"),
273 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.ECDsa"),
274 FACADE_ASSEMBLY ("System.Security.Cryptography.Hashing"),
275 FACADE_ASSEMBLY ("System.Security.Cryptography.Hashing.Algorithms"),
276 FACADE_ASSEMBLY ("System.Security.Cryptography.OpenSsl"),
277 FACADE_ASSEMBLY ("System.Security.Cryptography.Pkcs"),
278 FACADE_ASSEMBLY ("System.Security.Cryptography.Primitives"),
279 FACADE_ASSEMBLY ("System.Security.Cryptography.ProtectedData"),
280 FACADE_ASSEMBLY ("System.Security.Cryptography.RSA"),
281 FACADE_ASSEMBLY ("System.Security.Cryptography.RandomNumberGenerator"),
282 FACADE_ASSEMBLY ("System.Security.Cryptography.X509Certificates"),
283 FACADE_ASSEMBLY ("System.Security.Principal"),
284 FACADE_ASSEMBLY ("System.Security.Principal.Windows"),
285 FACADE_ASSEMBLY ("System.Security.SecureString"),
286 {"System.ServiceModel", 3},
287 {"System.ServiceModel.Activation", 0},
288 {"System.ServiceModel.Discovery", 0},
289 FACADE_ASSEMBLY ("System.ServiceModel.Duplex"),
290 FACADE_ASSEMBLY ("System.ServiceModel.Http"),
291 FACADE_ASSEMBLY ("System.ServiceModel.NetTcp"),
292 FACADE_ASSEMBLY ("System.ServiceModel.Primitives"),
293 {"System.ServiceModel.Routing", 0},
294 FACADE_ASSEMBLY ("System.ServiceModel.Security"),
295 {"System.ServiceModel.Web", 2},
296 {"System.ServiceProcess", 0},
297 FACADE_ASSEMBLY ("System.ServiceProcess.ServiceController"),
298 FACADE_ASSEMBLY ("System.Text.Encoding"),
299 FACADE_ASSEMBLY ("System.Text.Encoding.CodePages"),
300 FACADE_ASSEMBLY ("System.Text.Encoding.Extensions"),
301 FACADE_ASSEMBLY ("System.Text.RegularExpressions"),
302 FACADE_ASSEMBLY ("System.Threading"),
303 FACADE_ASSEMBLY ("System.Threading.AccessControl"),
304 FACADE_ASSEMBLY ("System.Threading.Overlapped"),
305 FACADE_ASSEMBLY ("System.Threading.Tasks"),
306 {"System.Threading.Tasks.Dataflow", 0},
307 FACADE_ASSEMBLY ("System.Threading.Tasks.Extensions"),
308 FACADE_ASSEMBLY ("System.Threading.Tasks.Parallel"),
309 FACADE_ASSEMBLY ("System.Threading.Thread"),
310 FACADE_ASSEMBLY ("System.Threading.ThreadPool"),
311 FACADE_ASSEMBLY ("System.Threading.Timer"),
312 {"System.Transactions", 0},
313 FACADE_ASSEMBLY ("System.ValueTuple"),
314 {"System.Web", 0},
315 {"System.Web.Abstractions", 2},
316 {"System.Web.ApplicationServices", 0},
317 {"System.Web.DynamicData", 2},
318 {"System.Web.Extensions", 2},
319 {"System.Web.Extensions.Design", 0},
320 {"System.Web.Mobile", 0},
321 {"System.Web.RegularExpressions", 0},
322 {"System.Web.Routing", 2},
323 {"System.Web.Services", 0},
324 {"System.Windows", 0},
325 {"System.Windows.Forms", 0},
326 {"System.Windows.Forms.DataVisualization", 0},
327 {"System.Workflow.Activities", 0},
328 {"System.Workflow.ComponentModel", 0},
329 {"System.Workflow.Runtime", 0},
330 {"System.Xaml", 0},
331 {"System.Xml", 0},
332 {"System.Xml.Linq", 2},
333 FACADE_ASSEMBLY ("System.Xml.ReaderWriter"),
334 {"System.Xml.Serialization", 0},
335 FACADE_ASSEMBLY ("System.Xml.XDocument"),
336 FACADE_ASSEMBLY ("System.Xml.XPath"),
337 FACADE_ASSEMBLY ("System.Xml.XPath.XmlDocument"),
338 FACADE_ASSEMBLY ("System.Xml.XPath.XDocument"),
339 FACADE_ASSEMBLY ("System.Xml.XmlDocument"),
340 FACADE_ASSEMBLY ("System.Xml.XmlSerializer"),
341 FACADE_ASSEMBLY ("System.Xml.Xsl.Primitives"),
342 {"WindowsBase", 3},
343 {"cscompmgd", 0},
344 {"mscorlib", 0},
345 FACADE_ASSEMBLY ("netstandard"),
347 #endif
350 * keeps track of loaded assemblies
352 static GList *loaded_assemblies = NULL;
353 static MonoAssembly *corlib;
355 static char* unquote (const char *str);
357 /* This protects loaded_assemblies and image->references */
358 #define mono_assemblies_lock() mono_os_mutex_lock (&assemblies_mutex)
359 #define mono_assemblies_unlock() mono_os_mutex_unlock (&assemblies_mutex)
360 static mono_mutex_t assemblies_mutex;
362 /* If defined, points to the bundled assembly information */
363 static const MonoBundledAssembly **bundles;
365 static mono_mutex_t assembly_binding_mutex;
367 /* Loaded assembly binding info */
368 static GSList *loaded_assembly_bindings = NULL;
370 /* Class lazy loading functions */
371 static GENERATE_TRY_GET_CLASS_WITH_CACHE (internals_visible, "System.Runtime.CompilerServices", "InternalsVisibleToAttribute")
372 static MonoAssembly*
373 mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly, gboolean postload);
374 static MonoAssembly*
375 mono_assembly_request_byname_nosearch (MonoAssemblyName *aname, const MonoAssemblyByNameRequest *req, MonoImageOpenStatus *status);
376 static MonoAssembly*
377 mono_assembly_load_full_gac_base_default (MonoAssemblyName *aname, const char *basedir, MonoAssemblyContextKind asmctx, MonoImageOpenStatus *status);
378 static MonoAssembly*
379 chain_redirections_loadfrom (MonoImage *image, MonoImageOpenStatus *status);
380 static MonoAssembly*
381 mono_problematic_image_reprobe (MonoImage *image, MonoImageOpenStatus *status);
383 static MonoBoolean
384 mono_assembly_is_in_gac (const gchar *filanem);
385 static MonoAssemblyName*
386 mono_assembly_apply_binding (MonoAssemblyName *aname, MonoAssemblyName *dest_name);
388 static MonoAssembly*
389 prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly);
391 /* Assembly name matching */
392 static gboolean
393 exact_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name);
394 static gboolean
395 framework_assembly_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name);
397 static const char *
398 mono_asmctx_get_name (const MonoAssemblyContext *asmctx);
400 static gboolean
401 assembly_loadfrom_asmctx_from_path (const char *filename, MonoAssembly *requesting_assembly, gpointer user_data, MonoAssemblyContextKind *out_asmctx);
403 static gchar*
404 encode_public_tok (const guchar *token, gint32 len)
406 const static gchar allowed [] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
407 gchar *res;
408 int i;
410 res = (gchar *)g_malloc (len * 2 + 1);
411 for (i = 0; i < len; i++) {
412 res [i * 2] = allowed [token [i] >> 4];
413 res [i * 2 + 1] = allowed [token [i] & 0xF];
415 res [len * 2] = 0;
416 return res;
420 * mono_public_tokens_are_equal:
421 * \param pubt1 first public key token
422 * \param pubt2 second public key token
424 * Compare two public key tokens and return TRUE is they are equal and FALSE
425 * otherwise.
427 gboolean
428 mono_public_tokens_are_equal (const unsigned char *pubt1, const unsigned char *pubt2)
430 return memcmp (pubt1, pubt2, 16) == 0;
434 * mono_set_assemblies_path:
435 * \param path list of paths that contain directories where Mono will look for assemblies
437 * Use this method to override the standard assembly lookup system and
438 * override any assemblies coming from the GAC. This is the method
439 * that supports the \c MONO_PATH variable.
441 * Notice that \c MONO_PATH and this method are really a very bad idea as
442 * it prevents the GAC from working and it prevents the standard
443 * resolution mechanisms from working. Nonetheless, for some debugging
444 * situations and bootstrapping setups, this is useful to have.
446 void
447 mono_set_assemblies_path (const char* path)
449 char **splitted, **dest;
451 splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
452 if (assemblies_path)
453 g_strfreev (assemblies_path);
454 assemblies_path = dest = splitted;
455 while (*splitted) {
456 char *tmp = *splitted;
457 if (*tmp)
458 *dest++ = mono_path_canonicalize (tmp);
459 g_free (tmp);
460 splitted++;
462 *dest = *splitted;
464 if (g_hasenv ("MONO_DEBUG"))
465 return;
467 splitted = assemblies_path;
468 while (*splitted) {
469 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
470 g_warning ("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *splitted);
472 splitted++;
476 static void
477 check_path_env (void)
479 if (assemblies_path != NULL)
480 return;
482 char* path = g_getenv ("MONO_PATH");
483 if (!path)
484 return;
486 mono_set_assemblies_path(path);
487 g_free (path);
490 static void
491 check_extra_gac_path_env (void)
493 char *path;
494 char **splitted, **dest;
496 path = g_getenv ("MONO_GAC_PREFIX");
497 if (!path)
498 return;
500 splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
501 g_free (path);
503 if (extra_gac_paths)
504 g_strfreev (extra_gac_paths);
505 extra_gac_paths = dest = splitted;
506 while (*splitted){
507 if (**splitted)
508 *dest++ = *splitted;
509 splitted++;
511 *dest = *splitted;
513 if (!g_hasenv ("MONO_DEBUG"))
514 return;
516 while (*splitted) {
517 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
518 g_warning ("'%s' in MONO_GAC_PREFIX doesn't exist or has wrong permissions.", *splitted);
520 splitted++;
524 static gboolean
525 assembly_binding_maps_name (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname)
527 if (!info || !info->name)
528 return FALSE;
530 if (strcmp (info->name, aname->name))
531 return FALSE;
533 if (info->major != aname->major || info->minor != aname->minor)
534 return FALSE;
536 if ((info->culture != NULL && info->culture [0]) != (aname->culture != NULL && aname->culture [0]))
537 return FALSE;
539 if (info->culture && aname->culture && strcmp (info->culture, aname->culture))
540 return FALSE;
542 if (!mono_public_tokens_are_equal (info->public_key_token, aname->public_key_token))
543 return FALSE;
545 return TRUE;
548 static void
549 mono_assembly_binding_info_free (MonoAssemblyBindingInfo *info)
551 if (!info)
552 return;
554 g_free (info->name);
555 g_free (info->culture);
558 static void
559 get_publisher_policy_info (MonoImage *image, MonoAssemblyName *aname, MonoAssemblyBindingInfo *binding_info)
561 MonoTableInfo *t;
562 guint32 cols [MONO_MANIFEST_SIZE];
563 const gchar *filename;
564 gchar *subpath, *fullpath;
566 t = &image->tables [MONO_TABLE_MANIFESTRESOURCE];
567 /* MS Impl. accepts policy assemblies with more than
568 * one manifest resource, and only takes the first one */
569 if (t->rows < 1) {
570 binding_info->is_valid = FALSE;
571 return;
574 mono_metadata_decode_row (t, 0, cols, MONO_MANIFEST_SIZE);
575 if ((cols [MONO_MANIFEST_IMPLEMENTATION] & MONO_IMPLEMENTATION_MASK) != MONO_IMPLEMENTATION_FILE) {
576 binding_info->is_valid = FALSE;
577 return;
580 filename = mono_metadata_string_heap (image, cols [MONO_MANIFEST_NAME]);
581 g_assert (filename != NULL);
583 subpath = g_path_get_dirname (image->name);
584 fullpath = g_build_path (G_DIR_SEPARATOR_S, subpath, filename, NULL);
585 mono_config_parse_publisher_policy (fullpath, binding_info);
586 g_free (subpath);
587 g_free (fullpath);
589 /* Define the optional elements/attributes before checking */
590 if (!binding_info->culture)
591 binding_info->culture = g_strdup ("");
593 /* Check that the most important elements/attributes exist */
594 if (!binding_info->name || !binding_info->public_key_token [0] || !binding_info->has_old_version_bottom ||
595 !binding_info->has_new_version || !assembly_binding_maps_name (binding_info, aname)) {
596 mono_assembly_binding_info_free (binding_info);
597 binding_info->is_valid = FALSE;
598 return;
601 binding_info->is_valid = TRUE;
604 static int
605 compare_versions (AssemblyVersionSet *v, MonoAssemblyName *aname)
607 if (v->major > aname->major)
608 return 1;
609 else if (v->major < aname->major)
610 return -1;
612 if (v->minor > aname->minor)
613 return 1;
614 else if (v->minor < aname->minor)
615 return -1;
617 if (v->build > aname->build)
618 return 1;
619 else if (v->build < aname->build)
620 return -1;
622 if (v->revision > aname->revision)
623 return 1;
624 else if (v->revision < aname->revision)
625 return -1;
627 return 0;
630 static gboolean
631 check_policy_versions (MonoAssemblyBindingInfo *info, MonoAssemblyName *name)
633 if (!info->is_valid)
634 return FALSE;
636 /* If has_old_version_top doesn't exist, we don't have an interval */
637 if (!info->has_old_version_top) {
638 if (compare_versions (&info->old_version_bottom, name) == 0)
639 return TRUE;
641 return FALSE;
644 /* Check that the version defined by name is valid for the interval */
645 if (compare_versions (&info->old_version_top, name) < 0)
646 return FALSE;
648 /* We should be greater or equal than the small version */
649 if (compare_versions (&info->old_version_bottom, name) > 0)
650 return FALSE;
652 return TRUE;
656 * mono_assembly_names_equal:
657 * \param l first assembly
658 * \param r second assembly.
660 * Compares two \c MonoAssemblyName instances and returns whether they are equal.
662 * This compares the names, the cultures, the release version and their
663 * public tokens.
665 * \returns TRUE if both assembly names are equal.
667 gboolean
668 mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r)
670 return mono_assembly_names_equal_flags (l, r, MONO_ANAME_EQ_NONE);
674 * mono_assembly_names_equal_flags:
675 * \param l first assembly name
676 * \param r second assembly name
677 * \param flags flags that affect what is compared.
679 * Compares two \c MonoAssemblyName instances and returns whether they are equal.
681 * This compares the simple names and cultures and optionally the versions and
682 * public key tokens, depending on the \c flags.
684 * \returns TRUE if both assembly names are equal.
686 gboolean
687 mono_assembly_names_equal_flags (MonoAssemblyName *l, MonoAssemblyName *r, MonoAssemblyNameEqFlags flags)
689 g_assert (l != NULL);
690 g_assert (r != NULL);
692 if (!l->name || !r->name)
693 return FALSE;
695 if ((flags & MONO_ANAME_EQ_IGNORE_CASE) != 0 && g_strcasecmp (l->name, r->name))
696 return FALSE;
698 if ((flags & MONO_ANAME_EQ_IGNORE_CASE) == 0 && strcmp (l->name, r->name))
699 return FALSE;
701 if (l->culture && r->culture && strcmp (l->culture, r->culture))
702 return FALSE;
704 if ((l->major != r->major || l->minor != r->minor ||
705 l->build != r->build || l->revision != r->revision) &&
706 (flags & MONO_ANAME_EQ_IGNORE_VERSION) == 0)
707 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)))
708 return FALSE;
710 if (!l->public_key_token [0] || !r->public_key_token [0] || (flags & MONO_ANAME_EQ_IGNORE_PUBKEY) != 0)
711 return TRUE;
713 if (!mono_public_tokens_are_equal (l->public_key_token, r->public_key_token))
714 return FALSE;
716 return TRUE;
720 * assembly_names_compare_versions:
721 * \param l left assembly name
722 * \param r right assembly name
723 * \param maxcomps how many version components to compare, or -1 to compare all.
725 * \returns a negative if \p l is a lower version than \p r; a positive value
726 * if \p r is a lower version than \p l, or zero if \p l and \p r are equal
727 * versions (comparing upto \p maxcomps components).
729 * Components are \c major, \c minor, \c revision, and \c build. \p maxcomps 1 means just compare
730 * majors. 2 means majors then minors. etc.
732 static int
733 assembly_names_compare_versions (MonoAssemblyName *l, MonoAssemblyName *r, int maxcomps)
735 int i = 0;
736 if (maxcomps < 0) maxcomps = 4;
737 #define CMP(field) do { \
738 if (l-> field < r-> field && i < maxcomps) return -1; \
739 if (l-> field > r-> field && i < maxcomps) return 1; \
740 } while (0)
741 CMP (major);
742 ++i;
743 CMP (minor);
744 ++i;
745 CMP (revision);
746 ++i;
747 CMP (build);
748 #undef CMP
749 return 0;
753 * mono_assembly_request_prepare:
754 * \param req the request to be initialized
755 * \param req_size the size of the request structure
756 * \param asmctx the assembly load context kind
758 * Initialize an assembly loader request. The passed structure \p req must be
759 * of size \p req_size. Its state will be reset and the assembly context kind will be prefilled with \p asmctx.
761 void
762 mono_assembly_request_prepare (MonoAssemblyLoadRequest *req, size_t req_size, MonoAssemblyContextKind asmctx)
764 memset (req, 0, req_size);
765 req->asmctx = asmctx;
768 static MonoAssembly *
769 load_in_path (const char *basename, const char** search_path, const MonoAssemblyOpenRequest *req, MonoImageOpenStatus *status)
771 int i;
772 char *fullpath;
773 MonoAssembly *result;
775 for (i = 0; search_path [i]; ++i) {
776 fullpath = g_build_filename (search_path [i], basename, NULL);
777 result = mono_assembly_request_open (fullpath, req, status);
778 g_free (fullpath);
779 if (result)
780 return result;
782 return NULL;
786 * mono_assembly_setrootdir:
787 * \param root_dir The pathname of the root directory where we will locate assemblies
789 * This routine sets the internal default root directory for looking up
790 * assemblies.
792 * This is used by Windows installations to compute dynamically the
793 * place where the Mono assemblies are located.
796 void
797 mono_assembly_setrootdir (const char *root_dir)
800 * Override the MONO_ASSEMBLIES directory configured at compile time.
802 if (default_path [0])
803 g_free (default_path [0]);
804 default_path [0] = g_strdup (root_dir);
808 * mono_assembly_getrootdir:
810 * Obtains the root directory used for looking up assemblies.
812 * Returns: a string with the directory, this string should not be freed.
814 G_CONST_RETURN gchar *
815 mono_assembly_getrootdir (void)
817 return default_path [0];
821 * mono_native_getrootdir:
823 * Obtains the root directory used for looking up native libs (.so, .dylib).
825 * Returns: a string with the directory, this string should be freed by
826 * the caller.
828 gchar *
829 mono_native_getrootdir (void)
831 gchar* fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), mono_config_get_reloc_lib_dir(), NULL);
832 return fullpath;
836 * mono_set_dirs:
837 * \param assembly_dir the base directory for assemblies
838 * \param config_dir the base directory for configuration files
840 * This routine is used internally and by developers embedding
841 * the runtime into their own applications.
843 * There are a number of cases to consider: Mono as a system-installed
844 * package that is available on the location preconfigured or Mono in
845 * a relocated location.
847 * If you are using a system-installed Mono, you can pass NULL
848 * to both parameters. If you are not, you should compute both
849 * directory values and call this routine.
851 * The values for a given PREFIX are:
853 * assembly_dir: PREFIX/lib
854 * config_dir: PREFIX/etc
856 * Notice that embedders that use Mono in a relocated way must
857 * compute the location at runtime, as they will be in control
858 * of where Mono is installed.
860 void
861 mono_set_dirs (const char *assembly_dir, const char *config_dir)
863 if (assembly_dir == NULL)
864 assembly_dir = mono_config_get_assemblies_dir ();
865 if (config_dir == NULL)
866 config_dir = mono_config_get_cfg_dir ();
867 mono_assembly_setrootdir (assembly_dir);
868 mono_set_config_dir (config_dir);
871 #ifndef HOST_WIN32
873 static char *
874 compute_base (char *path)
876 char *p = strrchr (path, '/');
877 if (p == NULL)
878 return NULL;
880 /* Not a well known Mono executable, we are embedded, cant guess the base */
881 if (strcmp (p, "/mono") && strcmp (p, "/mono-boehm") && strcmp (p, "/mono-sgen") && strcmp (p, "/pedump") && strcmp (p, "/monodis"))
882 return NULL;
884 *p = 0;
885 p = strrchr (path, '/');
886 if (p == NULL)
887 return NULL;
889 if (strcmp (p, "/bin") != 0)
890 return NULL;
891 *p = 0;
892 return path;
895 static void
896 fallback (void)
898 mono_set_dirs (mono_config_get_assemblies_dir (), mono_config_get_cfg_dir ());
901 static G_GNUC_UNUSED void
902 set_dirs (char *exe)
904 char *base;
905 char *config, *lib, *mono;
906 struct stat buf;
907 const char *bindir;
910 * Only /usr prefix is treated specially
912 bindir = mono_config_get_bin_dir ();
913 g_assert (bindir);
914 if (strncmp (exe, bindir, strlen (bindir)) == 0 || (base = compute_base (exe)) == NULL){
915 fallback ();
916 return;
919 config = g_build_filename (base, "etc", NULL);
920 lib = g_build_filename (base, "lib", NULL);
921 mono = g_build_filename (lib, "mono/4.5", NULL); // FIXME: stop hardcoding 4.5 here
922 if (stat (mono, &buf) == -1)
923 fallback ();
924 else {
925 mono_set_dirs (lib, config);
928 g_free (config);
929 g_free (lib);
930 g_free (mono);
933 #endif /* HOST_WIN32 */
936 * mono_set_rootdir:
938 * Registers the root directory for the Mono runtime, for Linux and Solaris 10,
939 * this auto-detects the prefix where Mono was installed.
941 void
942 mono_set_rootdir (void)
944 #if defined(HOST_WIN32) || (defined(HOST_DARWIN) && !defined(TARGET_ARM))
945 gchar *bindir, *installdir, *root, *name, *resolvedname, *config;
947 #ifdef HOST_WIN32
948 name = mono_get_module_file_name ((HMODULE) &__ImageBase);
949 #else
952 * _NSGetExecutablePath may return -1 to indicate buf is not large
953 * enough, but we ignore that case to avoid having to do extra dynamic
954 * allocation for the path and hope that 4096 is enough - this is
955 * ok in the Linux/Solaris case below at least...
958 gchar buf[4096];
959 guint buf_size = sizeof (buf);
961 name = NULL;
962 if (_NSGetExecutablePath (buf, &buf_size) == 0)
963 name = g_strdup (buf);
965 if (name == NULL) {
966 fallback ();
967 return;
970 #endif
972 resolvedname = mono_path_resolve_symlinks (name);
974 bindir = g_path_get_dirname (resolvedname);
975 installdir = g_path_get_dirname (bindir);
976 root = g_build_path (G_DIR_SEPARATOR_S, installdir, "lib", NULL);
978 config = g_build_filename (root, "..", "etc", NULL);
979 #ifdef HOST_WIN32
980 mono_set_dirs (root, config);
981 #else
982 if (g_file_test (root, G_FILE_TEST_EXISTS) && g_file_test (config, G_FILE_TEST_EXISTS))
983 mono_set_dirs (root, config);
984 else
985 fallback ();
986 #endif
988 g_free (config);
989 g_free (root);
990 g_free (installdir);
991 g_free (bindir);
992 g_free (name);
993 g_free (resolvedname);
994 #elif defined(DISABLE_MONO_AUTODETECTION)
995 fallback ();
996 #else
997 char buf [4096];
998 int s;
999 char *str;
1001 #if defined(HAVE_READLINK)
1002 /* Linux style */
1003 s = readlink ("/proc/self/exe", buf, sizeof (buf)-1);
1004 #else
1005 s = -1;
1006 #endif
1008 if (s != -1){
1009 buf [s] = 0;
1010 set_dirs (buf);
1011 return;
1014 /* Solaris 10 style */
1015 str = g_strdup_printf ("/proc/%d/path/a.out", getpid ());
1017 #if defined(HAVE_READLINK)
1018 s = readlink (str, buf, sizeof (buf)-1);
1019 #else
1020 s = -1;
1021 #endif
1023 g_free (str);
1024 if (s != -1){
1025 buf [s] = 0;
1026 set_dirs (buf);
1027 return;
1029 fallback ();
1030 #endif
1034 * mono_assemblies_init:
1036 * Initialize global variables used by this module.
1038 void
1039 mono_assemblies_init (void)
1042 * Initialize our internal paths if we have not been initialized yet.
1043 * This happens when embedders use Mono.
1045 if (mono_assembly_getrootdir () == NULL)
1046 mono_set_rootdir ();
1048 check_path_env ();
1049 check_extra_gac_path_env ();
1051 mono_os_mutex_init_recursive (&assemblies_mutex);
1052 mono_os_mutex_init (&assembly_binding_mutex);
1054 #ifndef DISABLE_DESKTOP_LOADER
1055 assembly_remapping_table = g_hash_table_new (g_str_hash, g_str_equal);
1057 int i;
1058 for (i = 0; i < G_N_ELEMENTS (framework_assemblies); ++i)
1059 g_hash_table_insert (assembly_remapping_table, (void*)framework_assemblies [i].assembly_name, (void*)&framework_assemblies [i]);
1061 #endif
1062 mono_install_assembly_asmctx_from_path_hook (assembly_loadfrom_asmctx_from_path, NULL);
1066 static void
1067 mono_assembly_binding_lock (void)
1069 mono_locks_os_acquire (&assembly_binding_mutex, AssemblyBindingLock);
1072 static void
1073 mono_assembly_binding_unlock (void)
1075 mono_locks_os_release (&assembly_binding_mutex, AssemblyBindingLock);
1078 gboolean
1079 mono_assembly_fill_assembly_name_full (MonoImage *image, MonoAssemblyName *aname, gboolean copyBlobs)
1081 MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
1082 guint32 cols [MONO_ASSEMBLY_SIZE];
1083 gint32 machine, flags;
1085 if (!t->rows)
1086 return FALSE;
1088 mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
1090 aname->hash_len = 0;
1091 aname->hash_value = NULL;
1092 aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
1093 if (copyBlobs)
1094 aname->name = g_strdup (aname->name);
1095 aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
1096 if (copyBlobs)
1097 aname->culture = g_strdup (aname->culture);
1098 aname->flags = cols [MONO_ASSEMBLY_FLAGS];
1099 aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
1100 aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
1101 aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
1102 aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER];
1103 aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG];
1104 if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
1105 guchar* token = (guchar *)g_malloc (8);
1106 gchar* encoded;
1107 const gchar* pkey;
1108 int len;
1110 pkey = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
1111 len = mono_metadata_decode_blob_size (pkey, &pkey);
1112 aname->public_key = (guchar*)pkey;
1114 mono_digest_get_public_token (token, aname->public_key, len);
1115 encoded = encode_public_tok (token, 8);
1116 g_strlcpy ((char*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1118 g_free (encoded);
1119 g_free (token);
1121 else {
1122 aname->public_key = NULL;
1123 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1126 if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
1127 aname->public_key = (guchar*)mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
1128 if (copyBlobs) {
1129 const gchar *pkey_end;
1130 int len = mono_metadata_decode_blob_size ((const gchar*) aname->public_key, &pkey_end);
1131 pkey_end += len; /* move to end */
1132 size_t size = pkey_end - (const gchar*)aname->public_key;
1133 guchar *tmp = g_new (guchar, size);
1134 memcpy (tmp, aname->public_key, size);
1135 aname->public_key = tmp;
1139 else
1140 aname->public_key = 0;
1142 machine = image->image_info->cli_header.coff.coff_machine;
1143 flags = image->image_info->cli_cli_header.ch_flags;
1144 switch (machine) {
1145 case COFF_MACHINE_I386:
1146 /* https://bugzilla.xamarin.com/show_bug.cgi?id=17632 */
1147 if (flags & (CLI_FLAGS_32BITREQUIRED|CLI_FLAGS_PREFERRED32BIT))
1148 aname->arch = MONO_PROCESSOR_ARCHITECTURE_X86;
1149 else if ((flags & 0x70) == 0x70)
1150 aname->arch = MONO_PROCESSOR_ARCHITECTURE_NONE;
1151 else
1152 aname->arch = MONO_PROCESSOR_ARCHITECTURE_MSIL;
1153 break;
1154 case COFF_MACHINE_IA64:
1155 aname->arch = MONO_PROCESSOR_ARCHITECTURE_IA64;
1156 break;
1157 case COFF_MACHINE_AMD64:
1158 aname->arch = MONO_PROCESSOR_ARCHITECTURE_AMD64;
1159 break;
1160 case COFF_MACHINE_ARM:
1161 aname->arch = MONO_PROCESSOR_ARCHITECTURE_ARM;
1162 break;
1163 default:
1164 break;
1167 return TRUE;
1171 * mono_assembly_fill_assembly_name:
1172 * \param image Image
1173 * \param aname Name
1174 * \returns TRUE if successful
1176 gboolean
1177 mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname)
1179 return mono_assembly_fill_assembly_name_full (image, aname, FALSE);
1183 * mono_stringify_assembly_name:
1184 * \param aname the assembly name.
1186 * Convert \p aname into its string format. The returned string is dynamically
1187 * allocated and should be freed by the caller.
1189 * \returns a newly allocated string with a string representation of
1190 * the assembly name.
1192 char*
1193 mono_stringify_assembly_name (MonoAssemblyName *aname)
1195 const char *quote = (aname->name && g_ascii_isspace (aname->name [0])) ? "\"" : "";
1197 return g_strdup_printf (
1198 "%s%s%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s%s",
1199 quote, aname->name, quote,
1200 aname->major, aname->minor, aname->build, aname->revision,
1201 aname->culture && *aname->culture? aname->culture: "neutral",
1202 aname->public_key_token [0] ? (char *)aname->public_key_token : "null",
1203 (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) ? ", Retargetable=Yes" : "");
1206 static gchar*
1207 assemblyref_public_tok (MonoImage *image, guint32 key_index, guint32 flags)
1209 const gchar *public_tok;
1210 int len;
1212 public_tok = mono_metadata_blob_heap (image, key_index);
1213 len = mono_metadata_decode_blob_size (public_tok, &public_tok);
1215 if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
1216 guchar token [8];
1217 mono_digest_get_public_token (token, (guchar*)public_tok, len);
1218 return encode_public_tok (token, 8);
1221 return encode_public_tok ((guchar*)public_tok, len);
1224 static gchar*
1225 assemblyref_public_tok_checked (MonoImage *image, guint32 key_index, guint32 flags, MonoError *error)
1227 const gchar *public_tok;
1228 int len;
1230 public_tok = mono_metadata_blob_heap_checked (image, key_index, error);
1231 return_val_if_nok (error, NULL);
1232 len = mono_metadata_decode_blob_size (public_tok, &public_tok);
1234 if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
1235 guchar token [8];
1236 mono_digest_get_public_token (token, (guchar*)public_tok, len);
1237 return encode_public_tok (token, 8);
1239 return encode_public_tok ((guchar*)public_tok, len);
1243 * mono_assembly_addref:
1244 * \param assembly the assembly to reference
1246 * This routine increments the reference count on a MonoAssembly.
1247 * The reference count is reduced every time the method mono_assembly_close() is
1248 * invoked.
1250 void
1251 mono_assembly_addref (MonoAssembly *assembly)
1253 mono_atomic_inc_i32 (&assembly->ref_count);
1257 * CAUTION: This table must be kept in sync with
1258 * ivkm/reflect/Fusion.cs
1261 #define SILVERLIGHT_KEY "7cec85d7bea7798e"
1262 #define WINFX_KEY "31bf3856ad364e35"
1263 #define ECMA_KEY "b77a5c561934e089"
1264 #define MSFINAL_KEY "b03f5f7f11d50a3a"
1265 #define COMPACTFRAMEWORK_KEY "969db8053d3322ac"
1267 typedef struct {
1268 const char *name;
1269 const char *from;
1270 const char *to;
1271 } KeyRemapEntry;
1273 static KeyRemapEntry key_remap_table[] = {
1274 { "CustomMarshalers", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1275 { "Microsoft.CSharp", WINFX_KEY, MSFINAL_KEY },
1276 { "Microsoft.VisualBasic", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1277 { "System", SILVERLIGHT_KEY, ECMA_KEY },
1278 { "System", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1279 { "System.ComponentModel.Composition", WINFX_KEY, ECMA_KEY },
1280 { "System.ComponentModel.DataAnnotations", "ddd0da4d3e678217", WINFX_KEY },
1281 { "System.Core", SILVERLIGHT_KEY, ECMA_KEY },
1282 { "System.Core", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1283 { "System.Data", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1284 { "System.Data.DataSetExtensions", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1285 { "System.Drawing", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1286 { "System.Messaging", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1287 // FIXME: MS uses MSFINAL_KEY for .NET 4.5
1288 { "System.Net", SILVERLIGHT_KEY, MSFINAL_KEY },
1289 { "System.Numerics", WINFX_KEY, ECMA_KEY },
1290 { "System.Runtime.Serialization", SILVERLIGHT_KEY, ECMA_KEY },
1291 { "System.Runtime.Serialization", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1292 { "System.ServiceModel", WINFX_KEY, ECMA_KEY },
1293 { "System.ServiceModel", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1294 { "System.ServiceModel.Web", SILVERLIGHT_KEY, WINFX_KEY },
1295 { "System.Web.Services", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1296 { "System.Windows", SILVERLIGHT_KEY, MSFINAL_KEY },
1297 { "System.Windows.Forms", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1298 { "System.Xml", SILVERLIGHT_KEY, ECMA_KEY },
1299 { "System.Xml", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1300 { "System.Xml.Linq", WINFX_KEY, ECMA_KEY },
1301 { "System.Xml.Linq", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1302 { "System.Xml.Serialization", WINFX_KEY, ECMA_KEY }
1305 static void
1306 remap_keys (MonoAssemblyName *aname)
1308 int i;
1309 for (i = 0; i < G_N_ELEMENTS (key_remap_table); i++) {
1310 const KeyRemapEntry *entry = &key_remap_table [i];
1312 if (strcmp (aname->name, entry->name) ||
1313 !mono_public_tokens_are_equal (aname->public_key_token, (const unsigned char*) entry->from))
1314 continue;
1316 memcpy (aname->public_key_token, entry->to, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1318 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1319 "Remapped public key token of retargetable assembly %s from %s to %s",
1320 aname->name, entry->from, entry->to);
1321 return;
1325 static MonoAssemblyName *
1326 mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_aname)
1328 const MonoRuntimeInfo *current_runtime;
1330 if (aname->name == NULL) return aname;
1332 current_runtime = mono_get_runtime_info ();
1334 if (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) {
1335 const AssemblyVersionSet* vset;
1337 /* Remap to current runtime */
1338 vset = &current_runtime->version_sets [0];
1340 memcpy (dest_aname, aname, sizeof(MonoAssemblyName));
1341 dest_aname->major = vset->major;
1342 dest_aname->minor = vset->minor;
1343 dest_aname->build = vset->build;
1344 dest_aname->revision = vset->revision;
1345 dest_aname->flags &= ~ASSEMBLYREF_RETARGETABLE_FLAG;
1347 /* Remap assembly name */
1348 if (!strcmp (aname->name, "System.Net"))
1349 dest_aname->name = g_strdup ("System");
1351 remap_keys (dest_aname);
1353 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1354 "The request to load the retargetable assembly %s v%d.%d.%d.%d was remapped to %s v%d.%d.%d.%d",
1355 aname->name,
1356 aname->major, aname->minor, aname->build, aname->revision,
1357 dest_aname->name,
1358 vset->major, vset->minor, vset->build, vset->revision
1361 return dest_aname;
1364 #ifndef DISABLE_DESKTOP_LOADER
1365 const AssemblyVersionMap *vmap = (AssemblyVersionMap *)g_hash_table_lookup (assembly_remapping_table, aname->name);
1366 if (vmap) {
1367 const AssemblyVersionSet* vset;
1368 int index = vmap->version_set_index;
1369 g_assert (index < G_N_ELEMENTS (current_runtime->version_sets));
1370 vset = &current_runtime->version_sets [index];
1372 if (vmap->framework_facade_assembly) {
1373 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Assembly %s is a framework Facade asseembly",
1374 aname->name);
1375 return aname;
1378 if (aname->major == vset->major && aname->minor == vset->minor &&
1379 aname->build == vset->build && aname->revision == vset->revision) {
1380 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Found assembly remapping for %s and was for the same version %d.%d.%d.%d",
1381 aname->name,
1382 aname->major, aname->minor, aname->build, aname->revision);
1383 return aname;
1386 if (vmap->only_lower_versions && compare_versions ((AssemblyVersionSet*)vset, aname) < 0) {
1387 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY,
1388 "Found lower-versions-only assembly remaping to load %s %d.%d.%d.%d but mapping has %d.%d.%d.%d",
1389 aname->name,
1390 aname->major, aname->minor, aname->build, aname->revision,
1391 vset->major, vset->minor, vset->build, vset->revision
1393 return aname;
1396 if ((aname->major | aname->minor | aname->build | aname->revision) != 0)
1397 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
1398 "The request to load the assembly %s v%d.%d.%d.%d was remapped to v%d.%d.%d.%d",
1399 aname->name,
1400 aname->major, aname->minor, aname->build, aname->revision,
1401 vset->major, vset->minor, vset->build, vset->revision
1404 memcpy (dest_aname, aname, sizeof(MonoAssemblyName));
1405 dest_aname->major = vset->major;
1406 dest_aname->minor = vset->minor;
1407 dest_aname->build = vset->build;
1408 dest_aname->revision = vset->revision;
1409 if (vmap->new_assembly_name != NULL) {
1410 dest_aname->name = vmap->new_assembly_name;
1411 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
1412 "The assembly name %s was remapped to %s",
1413 aname->name,
1414 dest_aname->name);
1416 return dest_aname;
1418 #endif
1420 return aname;
1424 * mono_assembly_get_assemblyref:
1425 * \param image pointer to the \c MonoImage to extract the information from.
1426 * \param index index to the assembly reference in the image.
1427 * \param aname pointer to a \c MonoAssemblyName that will hold the returned value.
1429 * Fills out the \p aname with the assembly name of the \p index assembly reference in \p image.
1431 void
1432 mono_assembly_get_assemblyref (MonoImage *image, int index, MonoAssemblyName *aname)
1434 MonoTableInfo *t;
1435 guint32 cols [MONO_ASSEMBLYREF_SIZE];
1436 const char *hash;
1438 t = &image->tables [MONO_TABLE_ASSEMBLYREF];
1440 mono_metadata_decode_row (t, index, cols, MONO_ASSEMBLYREF_SIZE);
1442 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
1443 aname->hash_len = mono_metadata_decode_blob_size (hash, &hash);
1444 aname->hash_value = hash;
1445 aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
1446 aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
1447 aname->flags = cols [MONO_ASSEMBLYREF_FLAGS];
1448 aname->major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
1449 aname->minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
1450 aname->build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
1451 aname->revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
1453 if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
1454 gchar *token = assemblyref_public_tok (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname->flags);
1455 g_strlcpy ((char*)aname->public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1456 g_free (token);
1457 } else {
1458 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1462 static MonoAssembly*
1463 load_reference_by_aname_refonly_asmctx (MonoAssemblyName *aname, MonoAssembly *assm, MonoImageOpenStatus *status)
1465 MonoAssembly *reference = NULL;
1466 g_assert (assm != NULL);
1467 *status = MONO_IMAGE_OK;
1469 /* We use the loaded corlib */
1470 if (!strcmp (aname->name, MONO_ASSEMBLY_CORLIB_NAME)) {
1471 MonoAssemblyByNameRequest req;
1472 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
1473 req.requesting_assembly = assm;
1474 req.basedir = assm->basedir;
1475 reference = mono_assembly_request_byname (aname, &req, status);
1476 } else {
1477 reference = mono_assembly_loaded_full (aname, TRUE);
1478 if (!reference)
1479 /* Try a postload search hook */
1480 reference = mono_assembly_invoke_search_hook_internal (aname, assm, TRUE, TRUE);
1484 * Here we must advice that the error was due to
1485 * a non loaded reference using the ReflectionOnly api
1487 if (!reference)
1488 reference = (MonoAssembly *)REFERENCE_MISSING;
1490 return reference;
1493 static MonoAssembly*
1494 load_reference_by_aname_default_asmctx (MonoAssemblyName *aname, MonoAssembly *assm, MonoImageOpenStatus *status)
1496 MonoAssembly *reference = NULL;
1497 *status = MONO_IMAGE_OK;
1499 /* we first try without setting the basedir: this can eventually result in a ResolveAssembly
1500 * event which is the MS .net compatible behaviour (the assemblyresolve_event3.cs test has been fixed
1501 * accordingly, it would fail on the MS runtime before).
1502 * The second load attempt has the basedir set to keep compatibility with the old mono behavior, for
1503 * example bug-349190.2.cs and who knows how much more code in the wild.
1505 MonoAssemblyByNameRequest req;
1506 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
1507 req.requesting_assembly = assm;
1508 reference = mono_assembly_request_byname (aname, &req, status);
1509 if (!reference && assm) {
1510 memset (&req, 0, sizeof (req));
1511 req.request.asmctx = MONO_ASMCTX_DEFAULT;
1512 req.requesting_assembly = assm;
1513 req.basedir = assm->basedir;
1514 reference = mono_assembly_request_byname (aname, &req, status);
1517 return reference;
1520 static MonoAssembly*
1521 load_reference_by_aname_loadfrom_asmctx (MonoAssemblyName *aname, MonoAssembly *requesting, MonoImageOpenStatus *status)
1523 MonoAssembly *reference = NULL;
1524 MonoAssemblyByNameRequest req;
1525 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_LOADFROM);
1526 req.requesting_assembly = requesting;
1527 req.basedir = requesting->basedir;
1528 /* Just like default search, but look in the requesting assembly basedir right away */
1529 reference = mono_assembly_request_byname (aname, &req, status);
1530 return reference;
1534 static MonoAssembly*
1535 load_reference_by_aname_individual_asmctx (MonoAssemblyName *aname, MonoAssembly *requesting, MonoImageOpenStatus *status)
1537 /* For an individual assembly, all references must already be loaded or
1538 * else we fire the assembly resolve event - similar to refonly - but
1539 * subject to remaping and binding.
1542 MonoAssembly *reference = NULL;
1543 *status = MONO_IMAGE_OK;
1544 MonoAssemblyName maped_aname;
1545 MonoAssemblyName maped_name_pp;
1547 aname = mono_assembly_remap_version (aname, &maped_aname);
1548 aname = mono_assembly_apply_binding (aname, &maped_name_pp);
1550 reference = mono_assembly_loaded_full (aname, FALSE);
1551 /* Still try to load from application base directory, MONO_PATH or the
1552 * GAC. This is consistent with what .NET Framework (4.7) actually
1553 * does, rather than what the documentation implies: If `LoadFile` is
1554 * used to load an assembly into "no context"/individual assembly
1555 * context, the runtime will still load assemblies from the GAC or the
1556 * application base directory (e.g. `System.Runtime` will be loaded if
1557 * it wasn't already).
1558 * Moreover, those referenced assemblies are loaded in the default context.
1560 if (!reference) {
1561 MonoAssemblyByNameRequest req;
1562 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
1563 req.requesting_assembly = requesting;
1564 reference = mono_assembly_request_byname (aname, &req, status);
1566 if (!reference)
1567 reference = (MonoAssembly*)REFERENCE_MISSING;
1568 return reference;
1572 * mono_assembly_get_assemblyref:
1573 * \param image pointer to the \c MonoImage to extract the information from.
1574 * \param index index to the assembly reference in the image.
1575 * \param aname pointer to a \c MonoAssemblyName that will hold the returned value.
1576 * \param error set on error
1578 * Fills out the \p aname with the assembly name of the \p index assembly reference in \p image.
1580 * \returns TRUE on success, otherwise sets \p error and returns FALSE
1582 gboolean
1583 mono_assembly_get_assemblyref_checked (MonoImage *image, int index, MonoAssemblyName *aname, MonoError *error)
1585 MonoTableInfo *t;
1586 guint32 cols [MONO_ASSEMBLYREF_SIZE];
1587 const char *hash;
1589 t = &image->tables [MONO_TABLE_ASSEMBLYREF];
1591 if (!mono_metadata_decode_row_checked (image, t, index, cols, MONO_ASSEMBLYREF_SIZE, error))
1592 return FALSE;
1594 hash = mono_metadata_blob_heap_checked (image, cols [MONO_ASSEMBLYREF_HASH_VALUE], error);
1595 return_val_if_nok (error, FALSE);
1596 aname->hash_len = mono_metadata_decode_blob_size (hash, &hash);
1597 aname->hash_value = hash;
1598 aname->name = mono_metadata_string_heap_checked (image, cols [MONO_ASSEMBLYREF_NAME], error);
1599 return_val_if_nok (error, FALSE);
1600 aname->culture = mono_metadata_string_heap_checked (image, cols [MONO_ASSEMBLYREF_CULTURE], error);
1601 return_val_if_nok (error, FALSE);
1602 aname->flags = cols [MONO_ASSEMBLYREF_FLAGS];
1603 aname->major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
1604 aname->minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
1605 aname->build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
1606 aname->revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
1607 if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
1608 gchar *token = assemblyref_public_tok_checked (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname->flags, error);
1609 return_val_if_nok (error, FALSE);
1610 g_strlcpy ((char*)aname->public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1611 g_free (token);
1612 } else {
1613 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1615 return TRUE;
1619 * mono_assembly_load_reference:
1621 void
1622 mono_assembly_load_reference (MonoImage *image, int index)
1624 MonoAssembly *reference;
1625 MonoAssemblyName aname;
1626 MonoImageOpenStatus status;
1629 * image->references is shared between threads, so we need to access
1630 * it inside a critical section.
1632 mono_assemblies_lock ();
1633 if (!image->references) {
1634 MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLYREF];
1636 image->references = g_new0 (MonoAssembly *, t->rows + 1);
1637 image->nreferences = t->rows;
1639 reference = image->references [index];
1640 mono_assemblies_unlock ();
1641 if (reference)
1642 return;
1644 mono_assembly_get_assemblyref (image, index, &aname);
1646 if (image->assembly) {
1647 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
1648 char *aname_str = mono_stringify_assembly_name (&aname);
1649 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Loading reference %d of %s asmctx %s, looking for %s",
1650 index, image->name, mono_asmctx_get_name (&image->assembly->context),
1651 aname_str);
1652 g_free (aname_str);
1654 switch (mono_asmctx_get_kind (&image->assembly->context)) {
1655 case MONO_ASMCTX_DEFAULT:
1656 reference = load_reference_by_aname_default_asmctx (&aname, image->assembly, &status);
1657 break;
1658 case MONO_ASMCTX_REFONLY:
1659 reference = load_reference_by_aname_refonly_asmctx (&aname, image->assembly, &status);
1660 break;
1661 case MONO_ASMCTX_LOADFROM:
1662 reference = load_reference_by_aname_loadfrom_asmctx (&aname, image->assembly, &status);
1663 break;
1664 case MONO_ASMCTX_INDIVIDUAL:
1665 reference = load_reference_by_aname_individual_asmctx (&aname, image->assembly, &status);
1666 break;
1667 default:
1668 g_error ("Unexpected assembly load context kind %d for image %s.", mono_asmctx_get_kind (&image->assembly->context), image->name);
1669 break;
1671 } else {
1672 /* FIXME: can we establish that image->assembly is never NULL and this code is dead? */
1673 reference = load_reference_by_aname_default_asmctx (&aname, image->assembly, &status);
1676 if (reference == NULL){
1677 char *extra_msg;
1679 if (status == MONO_IMAGE_ERROR_ERRNO && errno == ENOENT) {
1680 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 : "" );
1681 } else if (status == MONO_IMAGE_ERROR_ERRNO) {
1682 extra_msg = g_strdup_printf ("System error: %s\n", strerror (errno));
1683 } else if (status == MONO_IMAGE_MISSING_ASSEMBLYREF) {
1684 extra_msg = g_strdup ("Cannot find an assembly referenced from this one.\n");
1685 } else if (status == MONO_IMAGE_IMAGE_INVALID) {
1686 extra_msg = g_strdup ("The file exists but is not a valid assembly.\n");
1687 } else {
1688 extra_msg = g_strdup ("");
1691 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, "The following assembly referenced from %s could not be loaded:\n"
1692 " Assembly: %s (assemblyref_index=%d)\n"
1693 " Version: %d.%d.%d.%d\n"
1694 " Public Key: %s\n%s",
1695 image->name, aname.name, index,
1696 aname.major, aname.minor, aname.build, aname.revision,
1697 strlen ((char*)aname.public_key_token) == 0 ? "(none)" : (char*)aname.public_key_token, extra_msg);
1698 g_free (extra_msg);
1702 mono_assemblies_lock ();
1703 if (reference == NULL) {
1704 /* Flag as not found */
1705 reference = (MonoAssembly *)REFERENCE_MISSING;
1708 if (!image->references [index]) {
1709 if (reference != REFERENCE_MISSING){
1710 mono_assembly_addref (reference);
1711 if (image->assembly)
1712 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Ref addref %s[%p] -> %s[%p]: %d",
1713 image->assembly->aname.name, image->assembly, reference->aname.name, reference, reference->ref_count);
1714 } else {
1715 if (image->assembly)
1716 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Failed to load assembly %s[%p].",
1717 image->assembly->aname.name, image->assembly);
1720 image->references [index] = reference;
1722 mono_assemblies_unlock ();
1724 if (image->references [index] != reference) {
1725 /* Somebody loaded it before us */
1726 mono_assembly_close (reference);
1731 * mono_assembly_load_references:
1732 * \param image
1733 * \param status
1734 * \deprecated There is no reason to use this method anymore, it does nothing
1736 * This method is now a no-op, it does nothing other than setting the \p status to \c MONO_IMAGE_OK
1738 void
1739 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
1741 /* This is a no-op now but it is part of the embedding API so we can't remove it */
1742 *status = MONO_IMAGE_OK;
1745 typedef struct AssemblyLoadHook AssemblyLoadHook;
1746 struct AssemblyLoadHook {
1747 AssemblyLoadHook *next;
1748 MonoAssemblyLoadFunc func;
1749 gpointer user_data;
1752 static AssemblyLoadHook *assembly_load_hook = NULL;
1755 * mono_assembly_invoke_load_hook:
1757 void
1758 mono_assembly_invoke_load_hook (MonoAssembly *ass)
1760 AssemblyLoadHook *hook;
1762 for (hook = assembly_load_hook; hook; hook = hook->next) {
1763 hook->func (ass, hook->user_data);
1768 * mono_install_assembly_load_hook:
1770 void
1771 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
1773 AssemblyLoadHook *hook;
1775 g_return_if_fail (func != NULL);
1777 hook = g_new0 (AssemblyLoadHook, 1);
1778 hook->func = func;
1779 hook->user_data = user_data;
1780 hook->next = assembly_load_hook;
1781 assembly_load_hook = hook;
1784 static void
1785 free_assembly_load_hooks (void)
1787 AssemblyLoadHook *hook, *next;
1789 for (hook = assembly_load_hook; hook; hook = next) {
1790 next = hook->next;
1791 g_free (hook);
1795 typedef struct AssemblySearchHook AssemblySearchHook;
1796 struct AssemblySearchHook {
1797 AssemblySearchHook *next;
1798 MonoAssemblySearchFunc func;
1799 gboolean refonly;
1800 gboolean postload;
1801 gpointer user_data;
1804 static AssemblySearchHook *assembly_search_hook = NULL;
1806 static MonoAssembly*
1807 mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly, gboolean postload)
1809 AssemblySearchHook *hook;
1811 for (hook = assembly_search_hook; hook; hook = hook->next) {
1812 if ((hook->refonly == refonly) && (hook->postload == postload)) {
1813 MonoAssembly *ass;
1815 * A little explanation is in order here.
1817 * The default postload search hook needs to know the requesting assembly to report it to managed code.
1818 * The embedding API exposes a search hook that doesn't take such argument.
1820 * The original fix would call the default search hook before all the registered ones and pass
1821 * the requesting assembly to it. It works but broke a very suddle embedding API aspect that some users
1822 * rely on. Which is the ordering between user hooks and the default runtime hook.
1824 * Registering the hook after mono_jit_init would let your hook run before the default one and
1825 * when using it to handle non standard app layouts this could save your app from a massive amount
1826 * of syscalls that the default hook does when probing all sorts of places. Slow targets with horrible IO
1827 * are all using this trick and if we broke this assumption they would be very disapointed at us.
1829 * So what's the fix? We register the default hook using regular means and special case it when iterating
1830 * over the registered hooks. This preserves ordering and enables managed resolve hooks to get the requesting
1831 * assembly.
1833 if (hook->func == (void*)mono_domain_assembly_postload_search)
1834 ass = mono_domain_assembly_postload_search (aname, requesting, refonly);
1835 else
1836 ass = hook->func (aname, hook->user_data);
1837 if (ass)
1838 return ass;
1842 return NULL;
1846 * mono_assembly_invoke_search_hook:
1848 MonoAssembly*
1849 mono_assembly_invoke_search_hook (MonoAssemblyName *aname)
1851 return mono_assembly_invoke_search_hook_internal (aname, NULL, FALSE, FALSE);
1854 static void
1855 mono_install_assembly_search_hook_internal (MonoAssemblySearchFunc func, gpointer user_data, gboolean refonly, gboolean postload)
1857 AssemblySearchHook *hook;
1859 g_return_if_fail (func != NULL);
1861 hook = g_new0 (AssemblySearchHook, 1);
1862 hook->func = func;
1863 hook->user_data = user_data;
1864 hook->refonly = refonly;
1865 hook->postload = postload;
1866 hook->next = assembly_search_hook;
1867 assembly_search_hook = hook;
1871 * mono_install_assembly_search_hook:
1873 void
1874 mono_install_assembly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1876 mono_install_assembly_search_hook_internal (func, user_data, FALSE, FALSE);
1879 static void
1880 free_assembly_search_hooks (void)
1882 AssemblySearchHook *hook, *next;
1884 for (hook = assembly_search_hook; hook; hook = next) {
1885 next = hook->next;
1886 g_free (hook);
1891 * mono_install_assembly_refonly_search_hook:
1893 void
1894 mono_install_assembly_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1896 mono_install_assembly_search_hook_internal (func, user_data, TRUE, FALSE);
1900 * mono_install_assembly_postload_search_hook:
1902 void
1903 mono_install_assembly_postload_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1905 mono_install_assembly_search_hook_internal (func, user_data, FALSE, TRUE);
1908 void
1909 mono_install_assembly_postload_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1911 mono_install_assembly_search_hook_internal (func, user_data, TRUE, TRUE);
1914 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
1915 struct AssemblyPreLoadHook {
1916 AssemblyPreLoadHook *next;
1917 MonoAssemblyPreLoadFunc func;
1918 gpointer user_data;
1921 static AssemblyPreLoadHook *assembly_preload_hook = NULL;
1922 static AssemblyPreLoadHook *assembly_refonly_preload_hook = NULL;
1924 static MonoAssembly *
1925 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **apath)
1927 AssemblyPreLoadHook *hook;
1928 MonoAssembly *assembly;
1930 for (hook = assembly_preload_hook; hook; hook = hook->next) {
1931 assembly = hook->func (aname, apath, hook->user_data);
1932 if (assembly != NULL)
1933 return assembly;
1936 return NULL;
1939 static MonoAssembly *
1940 invoke_assembly_refonly_preload_hook (MonoAssemblyName *aname, gchar **apath)
1942 AssemblyPreLoadHook *hook;
1943 MonoAssembly *assembly;
1945 for (hook = assembly_refonly_preload_hook; hook; hook = hook->next) {
1946 assembly = hook->func (aname, apath, hook->user_data);
1947 if (assembly != NULL)
1948 return assembly;
1951 return NULL;
1955 * mono_install_assembly_preload_hook:
1957 void
1958 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
1960 AssemblyPreLoadHook *hook;
1962 g_return_if_fail (func != NULL);
1964 hook = g_new0 (AssemblyPreLoadHook, 1);
1965 hook->func = func;
1966 hook->user_data = user_data;
1967 hook->next = assembly_preload_hook;
1968 assembly_preload_hook = hook;
1972 * mono_install_assembly_refonly_preload_hook:
1974 void
1975 mono_install_assembly_refonly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
1977 AssemblyPreLoadHook *hook;
1979 g_return_if_fail (func != NULL);
1981 hook = g_new0 (AssemblyPreLoadHook, 1);
1982 hook->func = func;
1983 hook->user_data = user_data;
1984 hook->next = assembly_refonly_preload_hook;
1985 assembly_refonly_preload_hook = hook;
1988 static void
1989 free_assembly_preload_hooks (void)
1991 AssemblyPreLoadHook *hook, *next;
1993 for (hook = assembly_preload_hook; hook; hook = next) {
1994 next = hook->next;
1995 g_free (hook);
1998 for (hook = assembly_refonly_preload_hook; hook; hook = next) {
1999 next = hook->next;
2000 g_free (hook);
2004 typedef struct AssemblyAsmCtxFromPathHook AssemblyAsmCtxFromPathHook;
2005 struct AssemblyAsmCtxFromPathHook {
2006 AssemblyAsmCtxFromPathHook *next;
2007 MonoAssemblyAsmCtxFromPathFunc func;
2008 gpointer user_data;
2011 static AssemblyAsmCtxFromPathHook *assembly_asmctx_from_path_hook = NULL;
2014 * mono_install_assembly_asmctx_from_path_hook:
2016 * \param func Hook function
2017 * \param user_data User data
2019 * Installs a hook function \p func that when called with an absolute path name
2020 * returns \c TRUE and writes to \c out_asmctx if an assembly that name would
2021 * be found by that asmctx. The hooks are called in the order from most
2022 * recently added to oldest.
2025 void
2026 mono_install_assembly_asmctx_from_path_hook (MonoAssemblyAsmCtxFromPathFunc func, gpointer user_data)
2028 g_return_if_fail (func != NULL);
2030 AssemblyAsmCtxFromPathHook *hook = g_new0 (AssemblyAsmCtxFromPathHook, 1);
2031 hook->func = func;
2032 hook->user_data = user_data;
2033 hook->next = assembly_asmctx_from_path_hook;
2034 assembly_asmctx_from_path_hook = hook;
2038 * mono_assembly_invoke_asmctx_from_path_hook:
2040 * \param absfname absolute path name
2041 * \param requesting_assembly the \c MonoAssembly that requested the load, may be \c NULL
2042 * \param out_asmctx assembly context kind, written on output
2044 * Invokes hooks to find the assembly context that would have searched for the
2045 * given assembly name. Writes to \p out_asmctx the assembly context kind from
2046 * the first hook to return \c TRUE. \returns \c TRUE if any hook wrote to \p
2047 * out_asmctx, or \c FALSE otherwise.
2049 static gboolean
2050 assembly_invoke_asmctx_from_path_hook (const char *absfname, MonoAssembly *requesting_assembly, MonoAssemblyContextKind *out_asmctx)
2052 g_assert (absfname);
2053 g_assert (out_asmctx);
2054 AssemblyAsmCtxFromPathHook *hook;
2056 for (hook = assembly_asmctx_from_path_hook; hook; hook = hook->next) {
2057 *out_asmctx = MONO_ASMCTX_INDIVIDUAL;
2058 if (hook->func (absfname, requesting_assembly, hook->user_data, out_asmctx))
2059 return TRUE;
2061 return FALSE;
2065 static void
2066 free_assembly_asmctx_from_path_hooks (void)
2068 AssemblyAsmCtxFromPathHook *hook, *next;
2070 for (hook = assembly_asmctx_from_path_hook; hook; hook = next) {
2071 next = hook->next;
2072 g_free (hook);
2076 static gchar *
2077 absolute_dir (const gchar *filename)
2079 gchar *cwd;
2080 gchar *mixed;
2081 gchar **parts;
2082 gchar *part;
2083 GList *list, *tmp;
2084 GString *result;
2085 gchar *res;
2086 gint i;
2088 if (g_path_is_absolute (filename)) {
2089 part = g_path_get_dirname (filename);
2090 res = g_strconcat (part, G_DIR_SEPARATOR_S, NULL);
2091 g_free (part);
2092 return res;
2095 cwd = g_get_current_dir ();
2096 mixed = g_build_filename (cwd, filename, NULL);
2097 parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
2098 g_free (mixed);
2099 g_free (cwd);
2101 list = NULL;
2102 for (i = 0; (part = parts [i]) != NULL; i++) {
2103 if (!strcmp (part, "."))
2104 continue;
2106 if (!strcmp (part, "..")) {
2107 if (list && list->next) /* Don't remove root */
2108 list = g_list_delete_link (list, list);
2109 } else {
2110 list = g_list_prepend (list, part);
2114 result = g_string_new ("");
2115 list = g_list_reverse (list);
2117 /* Ignores last data pointer, which should be the filename */
2118 for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next){
2119 if (tmp->data)
2120 g_string_append_printf (result, "%s%c", (char *) tmp->data,
2121 G_DIR_SEPARATOR);
2124 res = result->str;
2125 g_string_free (result, FALSE);
2126 g_list_free (list);
2127 g_strfreev (parts);
2128 if (*res == '\0') {
2129 g_free (res);
2130 return g_strdup (".");
2133 return res;
2136 /**
2137 * mono_assembly_open_from_bundle:
2138 * \param filename Filename requested
2139 * \param status return status code
2141 * This routine tries to open the assembly specified by \p filename from the
2142 * defined bundles, if found, returns the MonoImage for it, if not found
2143 * returns NULL
2145 MonoImage *
2146 mono_assembly_open_from_bundle (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
2148 int i;
2149 char *name;
2150 gchar *lowercase_filename;
2151 MonoImage *image = NULL;
2152 gboolean is_satellite = FALSE;
2154 * we do a very simple search for bundled assemblies: it's not a general
2155 * purpose assembly loading mechanism.
2158 if (!bundles)
2159 return NULL;
2161 lowercase_filename = g_utf8_strdown (filename, -1);
2162 is_satellite = g_str_has_suffix (lowercase_filename, ".resources.dll");
2163 g_free (lowercase_filename);
2164 name = g_path_get_basename (filename);
2165 mono_assemblies_lock ();
2166 for (i = 0; !image && bundles [i]; ++i) {
2167 if (strcmp (bundles [i]->name, is_satellite ? filename : name) == 0) {
2168 image = mono_image_open_from_data_internal ((char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly, FALSE, name);
2169 break;
2172 mono_assemblies_unlock ();
2173 if (image) {
2174 mono_image_addref (image);
2175 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Loader loaded assembly from bundle: '%s'.", is_satellite ? filename : name);
2176 g_free (name);
2177 return image;
2179 g_free (name);
2180 return NULL;
2184 * mono_assembly_open_full:
2185 * \param filename the file to load
2186 * \param status return status code
2187 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
2189 * This loads an assembly from the specified \p filename. The \p filename allows
2190 * a local URL (starting with a \c file:// prefix). If a file prefix is used, the
2191 * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file
2192 * is treated as a local path.
2194 * First, an attempt is made to load the assembly from the bundled executable (for those
2195 * deployments that have been done with the \c mkbundle tool or for scenarios where the
2196 * assembly has been registered as an embedded assembly). If this is not the case, then
2197 * the assembly is loaded from disk using `api:mono_image_open_full`.
2199 * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of
2200 * the assembly is made.
2202 * If \p refonly is set to true, then the assembly is loaded purely for inspection with
2203 * the \c System.Reflection API.
2205 * \returns NULL on error, with the \p status set to an error code, or a pointer
2206 * to the assembly.
2208 MonoAssembly *
2209 mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
2211 MonoAssembly *res;
2212 MONO_ENTER_GC_UNSAFE;
2213 MonoAssemblyOpenRequest req;
2214 mono_assembly_request_prepare (&req.request, sizeof (req), refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT);
2215 res = mono_assembly_request_open (filename, &req, status);
2216 MONO_EXIT_GC_UNSAFE;
2217 return res;
2220 static gboolean
2221 assembly_loadfrom_asmctx_from_path (const char *filename, MonoAssembly *requesting_assembly,
2222 gpointer user_data, MonoAssemblyContextKind *out_asmctx) {
2223 if (requesting_assembly && mono_asmctx_get_kind (&requesting_assembly->context) == MONO_ASMCTX_LOADFROM) {
2224 if (mono_path_filename_in_basedir (filename, requesting_assembly->basedir)) {
2225 *out_asmctx = MONO_ASMCTX_LOADFROM;
2226 return TRUE;
2229 return FALSE;
2232 MonoAssembly *
2233 mono_assembly_request_open (const char *filename, const MonoAssemblyOpenRequest *open_req,
2234 MonoImageOpenStatus *status)
2236 MonoImage *image;
2237 MonoAssembly *ass;
2238 MonoImageOpenStatus def_status;
2239 gchar *fname;
2240 gchar *new_fname;
2241 gboolean loaded_from_bundle;
2243 MonoAssemblyLoadRequest load_req;
2244 /* we will be overwriting the load request's asmctx.*/
2245 memcpy (&load_req, &open_req->request, sizeof (load_req));
2247 g_return_val_if_fail (filename != NULL, NULL);
2249 if (!status)
2250 status = &def_status;
2251 *status = MONO_IMAGE_OK;
2253 if (strncmp (filename, "file://", 7) == 0) {
2254 GError *gerror = NULL;
2255 gchar *uri = (gchar *) filename;
2256 gchar *tmpuri;
2259 * MS allows file://c:/... and fails on file://localhost/c:/...
2260 * They also throw an IndexOutOfRangeException if "file://"
2262 if (uri [7] != '/')
2263 uri = g_strdup_printf ("file:///%s", uri + 7);
2265 tmpuri = uri;
2266 uri = mono_escape_uri_string (tmpuri);
2267 fname = g_filename_from_uri (uri, NULL, &gerror);
2268 g_free (uri);
2270 if (tmpuri != filename)
2271 g_free (tmpuri);
2273 if (gerror != NULL) {
2274 g_warning ("%s\n", gerror->message);
2275 g_error_free (gerror);
2276 fname = g_strdup (filename);
2278 } else {
2279 fname = g_strdup (filename);
2282 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2283 "Assembly Loader probing location: '%s'.", fname);
2285 new_fname = NULL;
2286 if (!mono_assembly_is_in_gac (fname)) {
2287 ERROR_DECL (error);
2288 new_fname = mono_make_shadow_copy (fname, error);
2289 if (!is_ok (error)) {
2290 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2291 "Assembly Loader shadow copy error: %s.", mono_error_get_message (error));
2292 mono_error_cleanup (error);
2293 *status = MONO_IMAGE_IMAGE_INVALID;
2294 g_free (fname);
2295 return NULL;
2298 if (load_req.asmctx != MONO_ASMCTX_REFONLY) {
2299 MonoAssemblyContextKind out_asmctx;
2300 /* If the path belongs to the appdomain base dir or the
2301 * base dir of the requesting assembly, load the
2302 * assembly in the corresponding asmctx.
2304 if (assembly_invoke_asmctx_from_path_hook (fname, open_req->requesting_assembly, &out_asmctx))
2305 load_req.asmctx = out_asmctx;
2307 } else {
2308 if (load_req.asmctx != MONO_ASMCTX_REFONLY) {
2309 /* GAC assemblies always in default context or refonly context. */
2310 load_req.asmctx = MONO_ASMCTX_DEFAULT;
2313 if (new_fname && new_fname != fname) {
2314 g_free (fname);
2315 fname = new_fname;
2316 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2317 "Assembly Loader shadow-copied assembly to: '%s'.", fname);
2320 image = NULL;
2322 const gboolean refonly = load_req.asmctx == MONO_ASMCTX_REFONLY;
2323 /* for LoadFrom(string), LoadFile(string) and Load(byte[]), allow them
2324 * to load problematic images. Not sure if ReflectionOnlyLoad(string)
2325 * and ReflectionOnlyLoadFrom(string) should also be allowed - let's
2326 * say, yes.
2328 const gboolean load_from_context = load_req.asmctx == MONO_ASMCTX_LOADFROM || load_req.asmctx == MONO_ASMCTX_INDIVIDUAL || load_req.asmctx == MONO_ASMCTX_REFONLY;
2330 // If VM built with mkbundle
2331 loaded_from_bundle = FALSE;
2332 if (bundles != NULL) {
2333 image = mono_assembly_open_from_bundle (fname, status, refonly);
2334 loaded_from_bundle = image != NULL;
2337 if (!image)
2338 image = mono_image_open_a_lot (fname, status, refonly, load_from_context);
2340 if (!image){
2341 if (*status == MONO_IMAGE_OK)
2342 *status = MONO_IMAGE_ERROR_ERRNO;
2343 g_free (fname);
2344 return NULL;
2347 if (load_req.asmctx == MONO_ASMCTX_LOADFROM || load_req.asmctx == MONO_ASMCTX_INDIVIDUAL) {
2348 MonoAssembly *redirected_asm = NULL;
2349 MonoImageOpenStatus new_status = MONO_IMAGE_OK;
2350 if ((redirected_asm = chain_redirections_loadfrom (image, &new_status))) {
2351 mono_image_close (image);
2352 image = redirected_asm->image;
2353 mono_image_addref (image); /* so that mono_image_close, below, has something to do */
2354 /* fall thru to if (image->assembly) below */
2355 } else if (new_status != MONO_IMAGE_OK) {
2356 *status = new_status;
2357 mono_image_close (image);
2358 g_free (fname);
2359 return NULL;
2363 if (image->assembly) {
2364 /* We want to return the MonoAssembly that's already loaded,
2365 * but if we're using the strict assembly loader, we also need
2366 * to check that the previously loaded assembly matches the
2367 * predicate. It could be that we previously loaded a
2368 * different version that happens to have the filename that
2369 * we're currently probing. */
2370 if (mono_loader_get_strict_strong_names () &&
2371 load_req.predicate && !load_req.predicate (image->assembly, load_req.predicate_ud)) {
2372 mono_image_close (image);
2373 g_free (fname);
2374 return NULL;
2375 } else {
2376 /* Already loaded by another appdomain */
2377 mono_assembly_invoke_load_hook (image->assembly);
2378 mono_image_close (image);
2379 g_free (fname);
2380 return image->assembly;
2384 ass = mono_assembly_request_load_from (image, fname, &load_req, status);
2386 if (ass) {
2387 if (!loaded_from_bundle)
2388 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2389 "Assembly Loader loaded assembly from location: '%s'.", filename);
2390 if (!refonly)
2391 mono_config_for_assembly (ass->image);
2394 /* Clear the reference added by mono_image_open */
2395 mono_image_close (image);
2397 g_free (fname);
2399 return ass;
2402 static void
2403 free_item (gpointer val, gpointer user_data)
2405 g_free (val);
2409 * mono_assembly_load_friends:
2410 * \param ass an assembly
2412 * Load the list of friend assemblies that are allowed to access
2413 * the assembly's internal types and members. They are stored as assembly
2414 * names in custom attributes.
2416 * This is an internal method, we need this because when we load mscorlib
2417 * we do not have the internals visible cattr loaded yet,
2418 * so we need to load these after we initialize the runtime.
2420 * LOCKING: Acquires the assemblies lock plus the loader lock.
2422 void
2423 mono_assembly_load_friends (MonoAssembly* ass)
2425 ERROR_DECL (error);
2426 int i;
2427 MonoCustomAttrInfo* attrs;
2428 GSList *list;
2430 if (ass->friend_assembly_names_inited)
2431 return;
2433 attrs = mono_custom_attrs_from_assembly_checked (ass, FALSE, error);
2434 mono_error_assert_ok (error);
2435 if (!attrs) {
2436 mono_assemblies_lock ();
2437 ass->friend_assembly_names_inited = TRUE;
2438 mono_assemblies_unlock ();
2439 return;
2442 mono_assemblies_lock ();
2443 if (ass->friend_assembly_names_inited) {
2444 mono_assemblies_unlock ();
2445 return;
2447 mono_assemblies_unlock ();
2449 list = NULL;
2451 * We build the list outside the assemblies lock, the worse that can happen
2452 * is that we'll need to free the allocated list.
2454 for (i = 0; i < attrs->num_attrs; ++i) {
2455 MonoCustomAttrEntry *attr = &attrs->attrs [i];
2456 MonoAssemblyName *aname;
2457 const gchar *data;
2458 uint32_t data_length;
2459 gchar *data_with_terminator;
2460 /* Do some sanity checking */
2461 if (!attr->ctor || attr->ctor->klass != mono_class_try_get_internals_visible_class ())
2462 continue;
2463 if (attr->data_size < 4)
2464 continue;
2465 data = (const char*)attr->data;
2466 /* 0xFF means null string, see custom attr format */
2467 if (data [0] != 1 || data [1] != 0 || (data [2] & 0xFF) == 0xFF)
2468 continue;
2469 data_length = mono_metadata_decode_value (data + 2, &data);
2470 data_with_terminator = (char *)g_memdup (data, data_length + 1);
2471 data_with_terminator[data_length] = 0;
2472 aname = g_new0 (MonoAssemblyName, 1);
2473 /*g_print ("friend ass: %s\n", data);*/
2474 if (mono_assembly_name_parse_full (data_with_terminator, aname, TRUE, NULL, NULL)) {
2475 list = g_slist_prepend (list, aname);
2476 } else {
2477 g_free (aname);
2479 g_free (data_with_terminator);
2481 mono_custom_attrs_free (attrs);
2483 mono_assemblies_lock ();
2484 if (ass->friend_assembly_names_inited) {
2485 mono_assemblies_unlock ();
2486 g_slist_foreach (list, free_item, NULL);
2487 g_slist_free (list);
2488 return;
2490 ass->friend_assembly_names = list;
2492 /* Because of the double checked locking pattern above */
2493 mono_memory_barrier ();
2494 ass->friend_assembly_names_inited = TRUE;
2495 mono_assemblies_unlock ();
2498 struct HasReferenceAssemblyAttributeIterData {
2499 gboolean has_attr;
2502 static gboolean
2503 has_reference_assembly_attribute_iterator (MonoImage *image, guint32 typeref_scope_token, const char *nspace, const char *name, guint32 method_token, gpointer user_data)
2505 gboolean stop_scanning = FALSE;
2506 struct HasReferenceAssemblyAttributeIterData *iter_data = (struct HasReferenceAssemblyAttributeIterData*)user_data;
2508 if (!strcmp (name, "ReferenceAssemblyAttribute") && !strcmp (nspace, "System.Runtime.CompilerServices")) {
2509 /* Note we don't check the assembly name, same as coreCLR. */
2510 iter_data->has_attr = TRUE;
2511 stop_scanning = TRUE;
2514 return stop_scanning;
2518 * mono_assembly_has_reference_assembly_attribute:
2519 * \param assembly a MonoAssembly
2520 * \param error set on error.
2522 * \returns TRUE if \p assembly has the \c System.Runtime.CompilerServices.ReferenceAssemblyAttribute set.
2523 * On error returns FALSE and sets \p error.
2525 gboolean
2526 mono_assembly_has_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error)
2528 g_assert (assembly && assembly->image);
2529 /* .NET Framework appears to ignore the attribute on dynamic
2530 * assemblies, so don't call this function for dynamic assemblies. */
2531 g_assert (!image_is_dynamic (assembly->image));
2532 error_init (error);
2535 * This might be called during assembly loading, so do everything using the low-level
2536 * metadata APIs.
2539 struct HasReferenceAssemblyAttributeIterData iter_data = { FALSE };
2541 mono_assembly_metadata_foreach_custom_attr (assembly, &has_reference_assembly_attribute_iterator, &iter_data);
2543 return iter_data.has_attr;
2547 * chain_redirections_loadfrom:
2548 * \param image a MonoImage that we wanted to load using LoadFrom context
2549 * \param status set if there was an error opening the redirected image
2551 * Check if we need to open a different image instead of the given one for some reason.
2552 * Returns NULL and sets status to \c MONO_IMAGE_OK if the given image was good.
2554 * Otherwise returns the assembly that we opened instead or sets status if
2555 * there was a problem opening the redirected image.
2558 MonoAssembly*
2559 chain_redirections_loadfrom (MonoImage *image, MonoImageOpenStatus *out_status)
2561 MonoImageOpenStatus status = MONO_IMAGE_OK;
2562 MonoAssembly *redirected = NULL;
2564 redirected = mono_assembly_binding_applies_to_image (image, &status);
2565 if (redirected || status != MONO_IMAGE_OK) {
2566 *out_status = status;
2567 return redirected;
2570 redirected = mono_problematic_image_reprobe (image, &status);
2571 if (redirected || status != MONO_IMAGE_OK) {
2572 *out_status = status;
2573 return redirected;
2576 *out_status = MONO_IMAGE_OK;
2577 return NULL;
2581 * mono_assembly_binding_applies_to_image:
2582 * \param image The image whose assembly name we should check
2583 * \param status sets on error;
2585 * Get the \c MonoAssemblyName from the given \p image metadata and apply binding redirects to it.
2586 * If the resulting name is different from the name in the image, load that \c MonoAssembly instead
2588 * \returns the loaded \c MonoAssembly, or NULL if no binding redirection applied.
2591 MonoAssembly*
2592 mono_assembly_binding_applies_to_image (MonoImage* image, MonoImageOpenStatus *status)
2594 /* This is a "fun" one now.
2595 * For LoadFrom ("/basedir/some.dll") or LoadFile("/basedir/some.dll") or Load(byte[])),
2596 * apparently what we're meant to do is:
2597 * 1. probe the assembly name from some.dll (or the byte array)
2598 * 2. apply binding redirects
2599 * 3. If we get some other different name, drop this image and use
2600 * the binding redirected name to probe.
2601 * 4. Return the new assembly.
2603 MonoAssemblyName probed_aname, dest_name;
2604 if (!mono_assembly_fill_assembly_name_full (image, &probed_aname, TRUE)) {
2605 if (*status == MONO_IMAGE_OK)
2606 *status = MONO_IMAGE_IMAGE_INVALID;
2607 return NULL;
2609 MonoAssembly *result_ass = NULL;
2610 MonoAssemblyName *result_name = &probed_aname;
2611 result_name = mono_assembly_apply_binding (result_name, &dest_name);
2612 if (result_name != &probed_aname && !mono_assembly_names_equal (result_name, &probed_aname)) {
2613 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
2614 char *probed_fullname = mono_stringify_assembly_name (&probed_aname);
2615 char *result_fullname = mono_stringify_assembly_name (result_name);
2616 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Request to load from %s in (%s) remapped to %s", probed_fullname, image->name, result_fullname);
2617 g_free (probed_fullname);
2618 g_free (result_fullname);
2620 const char *new_basedir = NULL; /* FIXME: null? - do a test of this */
2621 MonoAssemblyContextKind new_asmctx = MONO_ASMCTX_DEFAULT; /* FIXME: default? or? */
2622 MonoAssembly *new_requesting = NULL; /* this seems okay */
2623 MonoImageOpenStatus new_status = MONO_IMAGE_OK;
2625 MonoAssemblyByNameRequest new_req;
2626 mono_assembly_request_prepare (&new_req.request, sizeof (new_req), new_asmctx);
2627 new_req.requesting_assembly = new_requesting;
2628 new_req.basedir = new_basedir;
2629 result_ass = mono_assembly_request_byname (result_name, &new_req, &new_status);
2631 if (result_ass && new_status == MONO_IMAGE_OK) {
2632 g_assert (result_ass->image->assembly != NULL);
2633 } else {
2634 *status = new_status;
2637 mono_assembly_name_free (&probed_aname);
2638 return result_ass;
2642 * mono_problematic_image_reprobe:
2643 * \param image A MonoImage
2644 * \param status set on error
2646 * If the given image is problematic for mono (see image.c), then try to load
2647 * by assembly name in the default context (which should succeed with Mono's
2648 * own implementations of those assemblies).
2650 * Returns NULL and sets status to MONO_IMAGE_OK if no redirect is needed.
2652 * Otherwise returns the assembly we were redirected to, or NULL and sets a
2653 * non-ok status on failure.
2655 * IMPORTANT NOTE: Don't call this if \c image was found by probing the search
2656 * path, you will end up in a loop and a stack overflow.
2658 MonoAssembly*
2659 mono_problematic_image_reprobe (MonoImage *image, MonoImageOpenStatus *status)
2661 if (G_LIKELY (!mono_is_problematic_image (image))) {
2662 *status = MONO_IMAGE_OK;
2663 return NULL;
2665 MonoAssemblyName probed_aname;
2666 if (!mono_assembly_fill_assembly_name_full (image, &probed_aname, TRUE)) {
2667 *status = MONO_IMAGE_IMAGE_INVALID;
2668 return NULL;
2670 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
2671 char *probed_fullname = mono_stringify_assembly_name (&probed_aname);
2672 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Requested to load from problematic image %s, probing instead for assembly with name %s", image->name, probed_fullname);
2673 g_free (probed_fullname);
2675 const char *new_basedir = NULL;
2676 MonoAssemblyContextKind new_asmctx = MONO_ASMCTX_DEFAULT;
2677 MonoAssembly *new_requesting = NULL;
2678 MonoImageOpenStatus new_status = MONO_IMAGE_OK;
2679 MonoAssemblyByNameRequest new_req;
2680 mono_assembly_request_prepare (&new_req.request, sizeof (new_req), new_asmctx);
2681 new_req.requesting_assembly = new_requesting;
2682 new_req.basedir = new_basedir;
2683 // Note: this interacts with mono_image_open_a_lot (). If the path from
2684 // which we tried to load the problematic image is among the probing
2685 // paths, the MonoImage will be in the hash of loaded images and we
2686 // would just get it back again here, except for the code there that
2687 // mitigates the situation. Instead
2688 MonoAssembly *result_ass = mono_assembly_request_byname (&probed_aname, &new_req, &new_status);
2690 if (! (result_ass && new_status == MONO_IMAGE_OK)) {
2691 *status = new_status;
2693 mono_assembly_name_free (&probed_aname);
2694 return result_ass;
2697 * mono_assembly_open:
2698 * \param filename Opens the assembly pointed out by this name
2699 * \param status return status code
2701 * This loads an assembly from the specified \p filename. The \p filename allows
2702 * a local URL (starting with a \c file:// prefix). If a file prefix is used, the
2703 * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file
2704 * is treated as a local path.
2706 * First, an attempt is made to load the assembly from the bundled executable (for those
2707 * deployments that have been done with the \c mkbundle tool or for scenarios where the
2708 * assembly has been registered as an embedded assembly). If this is not the case, then
2709 * the assembly is loaded from disk using `api:mono_image_open_full`.
2711 * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of
2712 * the assembly is made.
2714 * \returns a pointer to the \c MonoAssembly if \p filename contains a valid
2715 * assembly or NULL on error. Details about the error are stored in the
2716 * \p status variable.
2718 MonoAssembly *
2719 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
2721 MonoAssembly *res;
2722 MONO_ENTER_GC_UNSAFE;
2723 MonoAssemblyOpenRequest req;
2724 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
2725 res = mono_assembly_request_open (filename, &req, status);
2726 MONO_EXIT_GC_UNSAFE;
2727 return res;
2731 * mono_assembly_load_from_full:
2732 * \param image Image to load the assembly from
2733 * \param fname assembly name to associate with the assembly
2734 * \param status returns the status condition
2735 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
2737 * If the provided \p image has an assembly reference, it will process the given
2738 * image as an assembly with the given name.
2740 * Most likely you want to use the `api:mono_assembly_load_full` method instead.
2742 * Returns: A valid pointer to a \c MonoAssembly* on success and the \p status will be
2743 * set to \c MONO_IMAGE_OK; or NULL on error.
2745 * If there is an error loading the assembly the \p status will indicate the
2746 * reason with \p status being set to \c MONO_IMAGE_INVALID if the
2747 * image did not contain an assembly reference table.
2749 MonoAssembly *
2750 mono_assembly_load_from_full (MonoImage *image, const char*fname,
2751 MonoImageOpenStatus *status, gboolean refonly)
2753 MonoAssembly *res;
2754 MONO_ENTER_GC_UNSAFE;
2755 MonoAssemblyLoadRequest req;
2756 mono_assembly_request_prepare (&req, sizeof (req), refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT);
2757 res = mono_assembly_request_load_from (image, fname, &req, status);
2758 MONO_EXIT_GC_UNSAFE;
2759 return res;
2762 MonoAssembly *
2763 mono_assembly_request_load_from (MonoImage *image, const char *fname,
2764 const MonoAssemblyLoadRequest *req,
2765 MonoImageOpenStatus *status)
2767 MonoAssemblyContextKind asmctx;
2768 MonoAssemblyCandidatePredicate predicate;
2769 gpointer user_data;
2771 MonoAssembly *ass, *ass2;
2772 char *base_dir;
2774 asmctx = req->asmctx;
2775 predicate = req->predicate;
2776 user_data = req->predicate_ud;
2778 if (!image->tables [MONO_TABLE_ASSEMBLY].rows) {
2779 /* 'image' doesn't have a manifest -- maybe someone is trying to Assembly.Load a .netmodule */
2780 *status = MONO_IMAGE_IMAGE_INVALID;
2781 return NULL;
2784 #if defined (HOST_WIN32)
2786 gchar *tmp_fn;
2787 int i;
2789 tmp_fn = g_strdup (fname);
2790 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
2791 if (tmp_fn [i] == '/')
2792 tmp_fn [i] = '\\';
2795 base_dir = absolute_dir (tmp_fn);
2796 g_free (tmp_fn);
2798 #else
2799 base_dir = absolute_dir (fname);
2800 #endif
2803 * Create assembly struct, and enter it into the assembly cache
2805 ass = g_new0 (MonoAssembly, 1);
2806 ass->basedir = base_dir;
2807 ass->context.kind = asmctx;
2808 ass->image = image;
2810 MONO_PROFILER_RAISE (assembly_loading, (ass));
2812 mono_assembly_fill_assembly_name (image, &ass->aname);
2814 if (mono_defaults.corlib && strcmp (ass->aname.name, MONO_ASSEMBLY_CORLIB_NAME) == 0) {
2815 // MS.NET doesn't support loading other mscorlibs
2816 g_free (ass);
2817 g_free (base_dir);
2818 mono_image_addref (mono_defaults.corlib);
2819 *status = MONO_IMAGE_OK;
2820 return mono_defaults.corlib->assembly;
2823 /* Add a non-temporary reference because of ass->image */
2824 mono_image_addref (image);
2826 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image addref %s[%p] (asmctx %s) -> %s[%p]: %d", ass->aname.name, ass, mono_asmctx_get_name (&ass->context), image->name, image, image->ref_count);
2829 * The load hooks might take locks so we can't call them while holding the
2830 * assemblies lock.
2832 if (ass->aname.name && asmctx != MONO_ASMCTX_INDIVIDUAL) {
2833 /* FIXME: I think individual context should probably also look for an existing MonoAssembly here, we just need to pass the asmctx to the search hook so that it does a filename match (I guess?) */
2834 ass2 = mono_assembly_invoke_search_hook_internal (&ass->aname, NULL, asmctx == MONO_ASMCTX_REFONLY, FALSE);
2835 if (ass2) {
2836 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Image %s[%p] reusing existing assembly %s[%p]", ass->aname.name, ass, ass2->aname.name, ass2);
2837 g_free (ass);
2838 g_free (base_dir);
2839 mono_image_close (image);
2840 *status = MONO_IMAGE_OK;
2841 return ass2;
2845 /* We need to check for ReferenceAssmeblyAttribute before we
2846 * mark the assembly as loaded and before we fire the load
2847 * hook. Otherwise mono_domain_fire_assembly_load () in
2848 * appdomain.c will cache a mapping from the assembly name to
2849 * this image and we won't be able to look for a different
2850 * candidate. */
2852 if (asmctx != MONO_ASMCTX_REFONLY) {
2853 ERROR_DECL (refasm_error);
2854 if (mono_assembly_has_reference_assembly_attribute (ass, refasm_error)) {
2855 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image for assembly '%s' (%s) has ReferenceAssemblyAttribute, skipping", ass->aname.name, image->name);
2856 g_free (ass);
2857 g_free (base_dir);
2858 mono_image_close (image);
2859 *status = MONO_IMAGE_IMAGE_INVALID;
2860 return NULL;
2862 mono_error_cleanup (refasm_error);
2865 if (predicate && !predicate (ass, user_data)) {
2866 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate returned FALSE, skipping '%s' (%s)\n", ass->aname.name, image->name);
2867 g_free (ass);
2868 g_free (base_dir);
2869 mono_image_close (image);
2870 *status = MONO_IMAGE_IMAGE_INVALID;
2871 return NULL;
2874 mono_assemblies_lock ();
2876 /* If an assembly is loaded into an individual context, always return a
2877 * new MonoAssembly, even if another assembly with the same name has
2878 * already been loaded.
2880 if (image->assembly && asmctx != MONO_ASMCTX_INDIVIDUAL) {
2882 * This means another thread has already loaded the assembly, but not yet
2883 * called the load hooks so the search hook can't find the assembly.
2885 mono_assemblies_unlock ();
2886 ass2 = image->assembly;
2887 g_free (ass);
2888 g_free (base_dir);
2889 mono_image_close (image);
2890 *status = MONO_IMAGE_OK;
2891 return ass2;
2894 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Prepared to set up assembly '%s' (%s)", ass->aname.name, image->name);
2896 /* If asmctx is INDIVIDUAL, image->assembly might not be NULL, so don't
2897 * overwrite it. */
2898 if (image->assembly == NULL)
2899 image->assembly = ass;
2901 loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
2902 mono_assemblies_unlock ();
2904 #ifdef HOST_WIN32
2905 if (image->is_module_handle)
2906 mono_image_fixup_vtable (image);
2907 #endif
2909 mono_assembly_invoke_load_hook (ass);
2911 MONO_PROFILER_RAISE (assembly_loaded, (ass));
2913 return ass;
2917 * mono_assembly_load_from:
2918 * \param image Image to load the assembly from
2919 * \param fname assembly name to associate with the assembly
2920 * \param status return status code
2922 * If the provided \p image has an assembly reference, it will process the given
2923 * image as an assembly with the given name.
2925 * Most likely you want to use the `api:mono_assembly_load_full` method instead.
2927 * This is equivalent to calling `api:mono_assembly_load_from_full` with the
2928 * \p refonly parameter set to FALSE.
2929 * \returns A valid pointer to a \c MonoAssembly* on success and then \p status will be
2930 * set to \c MONO_IMAGE_OK; or NULL on error.
2932 * If there is an error loading the assembly the \p status will indicate the
2933 * reason with \p status being set to \c MONO_IMAGE_INVALID if the
2934 * image did not contain an assembly reference table.
2937 MonoAssembly *
2938 mono_assembly_load_from (MonoImage *image, const char *fname,
2939 MonoImageOpenStatus *status)
2941 MonoAssembly *res;
2942 MONO_ENTER_GC_UNSAFE;
2943 MonoAssemblyLoadRequest req;
2944 mono_assembly_request_prepare (&req, sizeof (req), MONO_ASMCTX_DEFAULT);
2945 res = mono_assembly_request_load_from (image, fname, &req, status);
2946 MONO_EXIT_GC_UNSAFE;
2947 return res;
2951 * mono_assembly_name_free:
2952 * \param aname assembly name to free
2954 * Frees the provided assembly name object.
2955 * (it does not frees the object itself, only the name members).
2957 void
2958 mono_assembly_name_free (MonoAssemblyName *aname)
2960 MONO_ENTER_GC_UNSAFE;
2961 mono_assembly_name_free_internal (aname);
2962 MONO_EXIT_GC_UNSAFE;
2965 void
2966 mono_assembly_name_free_internal (MonoAssemblyName *aname)
2968 MONO_REQ_GC_UNSAFE_MODE;
2970 if (aname == NULL)
2971 return;
2973 g_free ((void *) aname->name);
2974 g_free ((void *) aname->culture);
2975 g_free ((void *) aname->hash_value);
2976 g_free ((guint8*) aname->public_key);
2979 static gboolean
2980 parse_public_key (const gchar *key, gchar** pubkey, gboolean *is_ecma)
2982 const gchar *pkey;
2983 gchar header [16], val, *arr, *endp;
2984 gint i, j, offset, bitlen, keylen, pkeylen;
2986 //both pubkey and is_ecma are required arguments
2987 g_assert (pubkey && is_ecma);
2989 keylen = strlen (key) >> 1;
2990 if (keylen < 1)
2991 return FALSE;
2993 /* allow the ECMA standard key */
2994 if (strcmp (key, "00000000000000000400000000000000") == 0) {
2995 *pubkey = NULL;
2996 *is_ecma = TRUE;
2997 return TRUE;
2999 *is_ecma = FALSE;
3000 val = g_ascii_xdigit_value (key [0]) << 4;
3001 val |= g_ascii_xdigit_value (key [1]);
3002 switch (val) {
3003 case 0x00:
3004 if (keylen < 13)
3005 return FALSE;
3006 val = g_ascii_xdigit_value (key [24]);
3007 val |= g_ascii_xdigit_value (key [25]);
3008 if (val != 0x06)
3009 return FALSE;
3010 pkey = key + 24;
3011 break;
3012 case 0x06:
3013 pkey = key;
3014 break;
3015 default:
3016 return FALSE;
3019 /* We need the first 16 bytes
3020 * to check whether this key is valid or not */
3021 pkeylen = strlen (pkey) >> 1;
3022 if (pkeylen < 16)
3023 return FALSE;
3025 for (i = 0, j = 0; i < 16; i++) {
3026 header [i] = g_ascii_xdigit_value (pkey [j++]) << 4;
3027 header [i] |= g_ascii_xdigit_value (pkey [j++]);
3030 if (header [0] != 0x06 || /* PUBLICKEYBLOB (0x06) */
3031 header [1] != 0x02 || /* Version (0x02) */
3032 header [2] != 0x00 || /* Reserved (word) */
3033 header [3] != 0x00 ||
3034 (guint)(read32 (header + 8)) != 0x31415352) /* DWORD magic = RSA1 */
3035 return FALSE;
3037 /* Based on this length, we _should_ be able to know if the length is right */
3038 bitlen = read32 (header + 12) >> 3;
3039 if ((bitlen + 16 + 4) != pkeylen)
3040 return FALSE;
3042 arr = (gchar *)g_malloc (keylen + 4);
3043 /* Encode the size of the blob */
3044 mono_metadata_encode_value (keylen, &arr[0], &endp);
3045 offset = (gint)(endp-arr);
3047 for (i = offset, j = 0; i < keylen + offset; i++) {
3048 arr [i] = g_ascii_xdigit_value (key [j++]) << 4;
3049 arr [i] |= g_ascii_xdigit_value (key [j++]);
3052 *pubkey = arr;
3054 return TRUE;
3057 static gboolean
3058 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)
3060 gint major, minor, build, revision;
3061 gint len;
3062 gint version_parts;
3063 gchar *pkeyptr, *encoded, tok [8];
3065 memset (aname, 0, sizeof (MonoAssemblyName));
3067 if (version) {
3068 version_parts = sscanf (version, "%u.%u.%u.%u", &major, &minor, &build, &revision);
3069 if (version_parts < 2 || version_parts > 4)
3070 return FALSE;
3072 /* FIXME: we should set build & revision to -1 (instead of 0)
3073 if these are not set in the version string. That way, later on,
3074 we can still determine if these were specified. */
3075 aname->major = major;
3076 aname->minor = minor;
3077 if (version_parts >= 3)
3078 aname->build = build;
3079 else
3080 aname->build = 0;
3081 if (version_parts == 4)
3082 aname->revision = revision;
3083 else
3084 aname->revision = 0;
3087 aname->flags = flags;
3088 aname->arch = arch;
3089 aname->name = g_strdup (name);
3091 if (culture) {
3092 if (g_ascii_strcasecmp (culture, "neutral") == 0)
3093 aname->culture = g_strdup ("");
3094 else
3095 aname->culture = g_strdup (culture);
3098 if (token && strncmp (token, "null", 4) != 0) {
3099 char *lower;
3101 /* the constant includes the ending NULL, hence the -1 */
3102 if (strlen (token) != (MONO_PUBLIC_KEY_TOKEN_LENGTH - 1)) {
3103 mono_assembly_name_free (aname);
3104 return FALSE;
3106 lower = g_ascii_strdown (token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
3107 g_strlcpy ((char*)aname->public_key_token, lower, MONO_PUBLIC_KEY_TOKEN_LENGTH);
3108 g_free (lower);
3111 if (key) {
3112 gboolean is_ecma = FALSE;
3113 gchar *pkey = NULL;
3114 if (strcmp (key, "null") == 0 || !parse_public_key (key, &pkey, &is_ecma)) {
3115 mono_assembly_name_free (aname);
3116 return FALSE;
3119 if (is_ecma) {
3120 g_assert (pkey == NULL);
3121 aname->public_key = NULL;
3122 g_strlcpy ((gchar*)aname->public_key_token, "b77a5c561934e089", MONO_PUBLIC_KEY_TOKEN_LENGTH);
3123 return TRUE;
3126 len = mono_metadata_decode_blob_size ((const gchar *) pkey, (const gchar **) &pkeyptr);
3127 // We also need to generate the key token
3128 mono_digest_get_public_token ((guchar*) tok, (guint8*) pkeyptr, len);
3129 encoded = encode_public_tok ((guchar*) tok, 8);
3130 g_strlcpy ((gchar*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH);
3131 g_free (encoded);
3133 if (save_public_key)
3134 aname->public_key = (guint8*) pkey;
3135 else
3136 g_free (pkey);
3139 return TRUE;
3142 static gboolean
3143 parse_assembly_directory_name (const char *name, const char *dirname, MonoAssemblyName *aname)
3145 gchar **parts;
3146 gboolean res;
3148 parts = g_strsplit (dirname, "_", 3);
3149 if (!parts || !parts[0] || !parts[1] || !parts[2]) {
3150 g_strfreev (parts);
3151 return FALSE;
3154 res = build_assembly_name (name, parts[0], parts[1], parts[2], NULL, 0, 0, aname, FALSE);
3155 g_strfreev (parts);
3156 return res;
3159 static gboolean
3160 split_key_value (const gchar *pair, gchar **key, guint32 *keylen, gchar **value)
3162 char *eqsign = (char*)strchr (pair, '=');
3163 if (!eqsign) {
3164 *key = NULL;
3165 *keylen = 0;
3166 *value = NULL;
3167 return FALSE;
3170 *key = (gchar*)pair;
3171 *keylen = eqsign - *key;
3172 while (*keylen > 0 && g_ascii_isspace ((*key) [*keylen - 1]))
3173 (*keylen)--;
3174 *value = g_strstrip (eqsign + 1);
3175 return TRUE;
3178 gboolean
3179 mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboolean save_public_key, gboolean *is_version_defined, gboolean *is_token_defined)
3181 gchar *dllname;
3182 gchar *dllname_uq;
3183 gchar *version = NULL;
3184 gchar *version_uq;
3185 gchar *culture = NULL;
3186 gchar *culture_uq;
3187 gchar *token = NULL;
3188 gchar *token_uq;
3189 gchar *key = NULL;
3190 gchar *key_uq;
3191 gchar *retargetable = NULL;
3192 gchar *retargetable_uq;
3193 gchar *procarch;
3194 gchar *procarch_uq;
3195 gboolean res;
3196 gchar *value, *part_name;
3197 guint32 part_name_len;
3198 gchar **parts;
3199 gchar **tmp;
3200 gboolean version_defined;
3201 gboolean token_defined;
3202 guint32 flags = 0;
3203 guint32 arch = MONO_PROCESSOR_ARCHITECTURE_NONE;
3205 if (!is_version_defined)
3206 is_version_defined = &version_defined;
3207 *is_version_defined = FALSE;
3208 if (!is_token_defined)
3209 is_token_defined = &token_defined;
3210 *is_token_defined = FALSE;
3212 parts = tmp = g_strsplit (name, ",", 6);
3213 if (!tmp || !*tmp) {
3214 g_strfreev (tmp);
3215 return FALSE;
3218 dllname = g_strstrip (*tmp);
3220 tmp++;
3222 while (*tmp) {
3223 if (!split_key_value (g_strstrip (*tmp), &part_name, &part_name_len, &value))
3224 goto cleanup_and_fail;
3226 if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Version", part_name_len)) {
3227 *is_version_defined = TRUE;
3228 version = value;
3229 if (strlen (version) == 0) {
3230 goto cleanup_and_fail;
3232 tmp++;
3233 continue;
3236 if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Culture", part_name_len)) {
3237 culture = value;
3238 if (strlen (culture) == 0) {
3239 goto cleanup_and_fail;
3241 tmp++;
3242 continue;
3245 if (part_name_len == 14 && !g_ascii_strncasecmp (part_name, "PublicKeyToken", part_name_len)) {
3246 *is_token_defined = TRUE;
3247 token = value;
3248 if (strlen (token) == 0) {
3249 goto cleanup_and_fail;
3251 tmp++;
3252 continue;
3255 if (part_name_len == 9 && !g_ascii_strncasecmp (part_name, "PublicKey", part_name_len)) {
3256 key = value;
3257 if (strlen (key) == 0) {
3258 goto cleanup_and_fail;
3260 tmp++;
3261 continue;
3264 if (part_name_len == 12 && !g_ascii_strncasecmp (part_name, "Retargetable", part_name_len)) {
3265 retargetable = value;
3266 retargetable_uq = unquote (retargetable);
3267 if (retargetable_uq != NULL)
3268 retargetable = retargetable_uq;
3270 if (!g_ascii_strcasecmp (retargetable, "yes")) {
3271 flags |= ASSEMBLYREF_RETARGETABLE_FLAG;
3272 } else if (g_ascii_strcasecmp (retargetable, "no")) {
3273 g_free (retargetable_uq);
3274 goto cleanup_and_fail;
3277 g_free (retargetable_uq);
3278 tmp++;
3279 continue;
3282 if (part_name_len == 21 && !g_ascii_strncasecmp (part_name, "ProcessorArchitecture", part_name_len)) {
3283 procarch = value;
3284 procarch_uq = unquote (procarch);
3285 if (procarch_uq != NULL)
3286 procarch = procarch_uq;
3288 if (!g_ascii_strcasecmp (procarch, "MSIL"))
3289 arch = MONO_PROCESSOR_ARCHITECTURE_MSIL;
3290 else if (!g_ascii_strcasecmp (procarch, "X86"))
3291 arch = MONO_PROCESSOR_ARCHITECTURE_X86;
3292 else if (!g_ascii_strcasecmp (procarch, "IA64"))
3293 arch = MONO_PROCESSOR_ARCHITECTURE_IA64;
3294 else if (!g_ascii_strcasecmp (procarch, "AMD64"))
3295 arch = MONO_PROCESSOR_ARCHITECTURE_AMD64;
3296 else {
3297 g_free (procarch_uq);
3298 goto cleanup_and_fail;
3301 g_free (procarch_uq);
3302 tmp++;
3303 continue;
3306 g_strfreev (parts);
3307 return FALSE;
3310 /* if retargetable flag is set, then we must have a fully qualified name */
3311 if (retargetable != NULL && (version == NULL || culture == NULL || (key == NULL && token == NULL))) {
3312 goto cleanup_and_fail;
3315 dllname_uq = unquote (dllname);
3316 version_uq = unquote (version);
3317 culture_uq = unquote (culture);
3318 token_uq = unquote (token);
3319 key_uq = unquote (key);
3321 res = build_assembly_name (
3322 dllname_uq == NULL ? dllname : dllname_uq,
3323 version_uq == NULL ? version : version_uq,
3324 culture_uq == NULL ? culture : culture_uq,
3325 token_uq == NULL ? token : token_uq,
3326 key_uq == NULL ? key : key_uq,
3327 flags, arch, aname, save_public_key);
3329 g_free (dllname_uq);
3330 g_free (version_uq);
3331 g_free (culture_uq);
3332 g_free (token_uq);
3333 g_free (key_uq);
3335 g_strfreev (parts);
3336 return res;
3338 cleanup_and_fail:
3339 g_strfreev (parts);
3340 return FALSE;
3343 static char*
3344 unquote (const char *str)
3346 gint slen;
3347 const char *end;
3349 if (str == NULL)
3350 return NULL;
3352 slen = strlen (str);
3353 if (slen < 2)
3354 return NULL;
3356 if (*str != '\'' && *str != '\"')
3357 return NULL;
3359 end = str + slen - 1;
3360 if (*str != *end)
3361 return NULL;
3363 return g_strndup (str + 1, slen - 2);
3367 * mono_assembly_name_parse:
3368 * \param name name to parse
3369 * \param aname the destination assembly name
3371 * Parses an assembly qualified type name and assigns the name,
3372 * version, culture and token to the provided assembly name object.
3374 * \returns TRUE if the name could be parsed.
3376 gboolean
3377 mono_assembly_name_parse (const char *name, MonoAssemblyName *aname)
3379 return mono_assembly_name_parse_full (name, aname, FALSE, NULL, NULL);
3383 * mono_assembly_name_new:
3384 * \param name name to parse
3386 * Allocate a new \c MonoAssemblyName and fill its values from the
3387 * passed \p name.
3389 * \returns a newly allocated structure or NULL if there was any failure.
3391 MonoAssemblyName*
3392 mono_assembly_name_new (const char *name)
3394 MonoAssemblyName *result = NULL;
3395 MONO_ENTER_GC_UNSAFE;
3396 MonoAssemblyName *aname = g_new0 (MonoAssemblyName, 1);
3397 if (mono_assembly_name_parse (name, aname))
3398 result = aname;
3399 else
3400 g_free (aname);
3401 MONO_EXIT_GC_UNSAFE;
3402 return result;
3406 * mono_assembly_name_get_name:
3408 const char*
3409 mono_assembly_name_get_name (MonoAssemblyName *aname)
3411 const char *result = NULL;
3412 MONO_ENTER_GC_UNSAFE;
3413 result = aname->name;
3414 MONO_EXIT_GC_UNSAFE;
3415 return result;
3419 * mono_assembly_name_get_culture:
3421 const char*
3422 mono_assembly_name_get_culture (MonoAssemblyName *aname)
3424 const char *result = NULL;
3425 MONO_ENTER_GC_UNSAFE;
3426 result = aname->culture;
3427 MONO_EXIT_GC_UNSAFE;
3428 return result;
3432 * mono_assembly_name_get_pubkeytoken:
3434 mono_byte*
3435 mono_assembly_name_get_pubkeytoken (MonoAssemblyName *aname)
3437 if (aname->public_key_token [0])
3438 return aname->public_key_token;
3439 return NULL;
3443 * mono_assembly_name_get_version:
3445 uint16_t
3446 mono_assembly_name_get_version (MonoAssemblyName *aname, uint16_t *minor, uint16_t *build, uint16_t *revision)
3448 if (minor)
3449 *minor = aname->minor;
3450 if (build)
3451 *build = aname->build;
3452 if (revision)
3453 *revision = aname->revision;
3454 return aname->major;
3457 static MonoAssembly*
3458 probe_for_partial_name (const char *basepath, const char *fullname, MonoAssemblyName *aname, MonoImageOpenStatus *status)
3460 gchar *fullpath = NULL;
3461 GDir *dirhandle;
3462 const char* direntry;
3463 MonoAssemblyName gac_aname;
3464 gint major=-1, minor=0, build=0, revision=0;
3465 gboolean exact_version;
3467 dirhandle = g_dir_open (basepath, 0, NULL);
3468 if (!dirhandle)
3469 return NULL;
3471 exact_version = (aname->major | aname->minor | aname->build | aname->revision) != 0;
3473 while ((direntry = g_dir_read_name (dirhandle))) {
3474 gboolean match = TRUE;
3476 if(!parse_assembly_directory_name (aname->name, direntry, &gac_aname))
3477 continue;
3479 if (aname->culture != NULL && strcmp (aname->culture, gac_aname.culture) != 0)
3480 match = FALSE;
3482 if (match && strlen ((char*)aname->public_key_token) > 0 &&
3483 !mono_public_tokens_are_equal (aname->public_key_token, gac_aname.public_key_token))
3484 match = FALSE;
3486 if (match) {
3487 if (exact_version) {
3488 match = (aname->major == gac_aname.major && aname->minor == gac_aname.minor &&
3489 aname->build == gac_aname.build && aname->revision == gac_aname.revision);
3491 else if (gac_aname.major < major)
3492 match = FALSE;
3493 else if (gac_aname.major == major) {
3494 if (gac_aname.minor < minor)
3495 match = FALSE;
3496 else if (gac_aname.minor == minor) {
3497 if (gac_aname.build < build)
3498 match = FALSE;
3499 else if (gac_aname.build == build && gac_aname.revision <= revision)
3500 match = FALSE;
3505 if (match) {
3506 major = gac_aname.major;
3507 minor = gac_aname.minor;
3508 build = gac_aname.build;
3509 revision = gac_aname.revision;
3510 g_free (fullpath);
3511 fullpath = g_build_path (G_DIR_SEPARATOR_S, basepath, direntry, fullname, NULL);
3514 mono_assembly_name_free (&gac_aname);
3517 g_dir_close (dirhandle);
3519 if (fullpath == NULL)
3520 return NULL;
3521 else {
3522 MonoAssemblyOpenRequest req;
3523 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
3524 MonoAssembly *res = mono_assembly_request_open (fullpath, &req, status);
3525 g_free (fullpath);
3526 return res;
3531 * mono_assembly_load_with_partial_name:
3532 * \param name an assembly name that is then parsed by `api:mono_assembly_name_parse`.
3533 * \param status return status code
3535 * Loads a \c MonoAssembly from a name. The name is parsed using `api:mono_assembly_name_parse`,
3536 * so it might contain a qualified type name, version, culture and token.
3538 * This will load the assembly from the file whose name is derived from the assembly name
3539 * by appending the \c .dll extension.
3541 * The assembly is loaded from either one of the extra Global Assembly Caches specified
3542 * by the extra GAC paths (specified by the \c MONO_GAC_PREFIX environment variable) or
3543 * if that fails from the GAC.
3545 * \returns NULL on failure, or a pointer to a \c MonoAssembly on success.
3547 MonoAssembly*
3548 mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status)
3550 MonoAssembly *result;
3551 MONO_ENTER_GC_UNSAFE;
3552 result = mono_assembly_load_with_partial_name_internal (name, status);
3553 MONO_EXIT_GC_UNSAFE;
3554 return result;
3557 MonoAssembly*
3558 mono_assembly_load_with_partial_name_internal (const char *name, MonoImageOpenStatus *status)
3560 ERROR_DECL (error);
3561 MonoAssembly *res;
3562 MonoAssemblyName *aname, base_name;
3563 MonoAssemblyName mapped_aname;
3564 gchar *fullname, *gacpath;
3565 gchar **paths;
3567 MONO_REQ_GC_UNSAFE_MODE;
3569 memset (&base_name, 0, sizeof (MonoAssemblyName));
3570 aname = &base_name;
3572 if (!mono_assembly_name_parse (name, aname))
3573 return NULL;
3576 * If no specific version has been requested, make sure we load the
3577 * correct version for system assemblies.
3579 if ((aname->major | aname->minor | aname->build | aname->revision) == 0)
3580 aname = mono_assembly_remap_version (aname, &mapped_aname);
3582 res = mono_assembly_loaded_full (aname, FALSE);
3583 if (res) {
3584 mono_assembly_name_free (aname);
3585 return res;
3588 res = invoke_assembly_preload_hook (aname, assemblies_path);
3589 if (res) {
3590 res->in_gac = FALSE;
3591 mono_assembly_name_free (aname);
3592 return res;
3595 fullname = g_strdup_printf ("%s.dll", aname->name);
3597 if (extra_gac_paths) {
3598 paths = extra_gac_paths;
3599 while (!res && *paths) {
3600 gacpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", aname->name, NULL);
3601 res = probe_for_partial_name (gacpath, fullname, aname, status);
3602 g_free (gacpath);
3603 paths++;
3607 if (res) {
3608 res->in_gac = TRUE;
3609 g_free (fullname);
3610 mono_assembly_name_free (aname);
3611 return res;
3614 gacpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), "mono", "gac", aname->name, NULL);
3615 res = probe_for_partial_name (gacpath, fullname, aname, status);
3616 g_free (gacpath);
3618 g_free (fullname);
3619 mono_assembly_name_free (aname);
3621 if (res)
3622 res->in_gac = TRUE;
3623 else {
3624 MonoDomain *domain = mono_domain_get ();
3626 res = mono_try_assembly_resolve (domain, name, NULL, FALSE, error);
3627 if (!is_ok (error)) {
3628 mono_error_cleanup (error);
3629 if (*status == MONO_IMAGE_OK)
3630 *status = MONO_IMAGE_IMAGE_INVALID;
3634 return res;
3637 static MonoBoolean
3638 mono_assembly_is_in_gac (const gchar *filename)
3640 const gchar *rootdir;
3641 gchar *gp;
3642 gchar **paths;
3644 if (filename == NULL)
3645 return FALSE;
3647 for (paths = extra_gac_paths; paths && *paths; paths++) {
3648 if (strstr (*paths, filename) != *paths)
3649 continue;
3651 gp = (gchar *) (filename + strlen (*paths));
3652 if (*gp != G_DIR_SEPARATOR)
3653 continue;
3654 gp++;
3655 if (strncmp (gp, "lib", 3))
3656 continue;
3657 gp += 3;
3658 if (*gp != G_DIR_SEPARATOR)
3659 continue;
3660 gp++;
3661 if (strncmp (gp, "mono", 4))
3662 continue;
3663 gp += 4;
3664 if (*gp != G_DIR_SEPARATOR)
3665 continue;
3666 gp++;
3667 if (strncmp (gp, "gac", 3))
3668 continue;
3669 gp += 3;
3670 if (*gp != G_DIR_SEPARATOR)
3671 continue;
3673 return TRUE;
3676 rootdir = mono_assembly_getrootdir ();
3677 if (strstr (filename, rootdir) != filename)
3678 return FALSE;
3680 gp = (gchar *) (filename + strlen (rootdir));
3681 if (*gp != G_DIR_SEPARATOR)
3682 return FALSE;
3683 gp++;
3684 if (strncmp (gp, "mono", 4))
3685 return FALSE;
3686 gp += 4;
3687 if (*gp != G_DIR_SEPARATOR)
3688 return FALSE;
3689 gp++;
3690 if (strncmp (gp, "gac", 3))
3691 return FALSE;
3692 gp += 3;
3693 if (*gp != G_DIR_SEPARATOR)
3694 return FALSE;
3695 return TRUE;
3698 static MonoImage*
3699 mono_assembly_load_publisher_policy (MonoAssemblyName *aname)
3701 MonoImage *image;
3702 gchar *filename, *pname, *name, *culture, *version, *fullpath, *subpath;
3703 gchar **paths;
3704 gint32 len;
3706 if (strstr (aname->name, ".dll")) {
3707 len = strlen (aname->name) - 4;
3708 name = (gchar *)g_malloc (len + 1);
3709 memcpy (name, aname->name, len);
3710 name[len] = 0;
3711 } else
3712 name = g_strdup (aname->name);
3714 if (aname->culture)
3715 culture = g_utf8_strdown (aname->culture, -1);
3716 else
3717 culture = g_strdup ("");
3719 pname = g_strdup_printf ("policy.%d.%d.%s", aname->major, aname->minor, name);
3720 version = g_strdup_printf ("0.0.0.0_%s_%s", culture, aname->public_key_token);
3721 g_free (name);
3722 g_free (culture);
3724 filename = g_strconcat (pname, ".dll", NULL);
3725 subpath = g_build_path (G_DIR_SEPARATOR_S, pname, version, filename, NULL);
3726 g_free (pname);
3727 g_free (version);
3728 g_free (filename);
3730 image = NULL;
3731 if (extra_gac_paths) {
3732 paths = extra_gac_paths;
3733 while (!image && *paths) {
3734 fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths,
3735 "lib", "mono", "gac", subpath, NULL);
3736 image = mono_image_open (fullpath, NULL);
3737 g_free (fullpath);
3738 paths++;
3742 if (image) {
3743 g_free (subpath);
3744 return image;
3747 fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
3748 "mono", "gac", subpath, NULL);
3749 image = mono_image_open (fullpath, NULL);
3750 g_free (subpath);
3751 g_free (fullpath);
3753 return image;
3756 static MonoAssemblyName*
3757 mono_assembly_bind_version (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname, MonoAssemblyName *dest_name)
3759 memcpy (dest_name, aname, sizeof (MonoAssemblyName));
3760 dest_name->major = info->new_version.major;
3761 dest_name->minor = info->new_version.minor;
3762 dest_name->build = info->new_version.build;
3763 dest_name->revision = info->new_version.revision;
3765 return dest_name;
3768 /* LOCKING: assembly_binding lock must be held */
3769 static MonoAssemblyBindingInfo*
3770 search_binding_loaded (MonoAssemblyName *aname)
3772 GSList *tmp;
3774 for (tmp = loaded_assembly_bindings; tmp; tmp = tmp->next) {
3775 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)tmp->data;
3776 if (assembly_binding_maps_name (info, aname))
3777 return info;
3780 return NULL;
3783 static inline gboolean
3784 info_compare_versions (AssemblyVersionSet *left, AssemblyVersionSet *right)
3786 if (left->major != right->major || left->minor != right->minor ||
3787 left->build != right->build || left->revision != right->revision)
3788 return FALSE;
3790 return TRUE;
3793 static inline gboolean
3794 info_versions_equal (MonoAssemblyBindingInfo *left, MonoAssemblyBindingInfo *right)
3796 if (left->has_old_version_bottom != right->has_old_version_bottom)
3797 return FALSE;
3799 if (left->has_old_version_top != right->has_old_version_top)
3800 return FALSE;
3802 if (left->has_new_version != right->has_new_version)
3803 return FALSE;
3805 if (left->has_old_version_bottom && !info_compare_versions (&left->old_version_bottom, &right->old_version_bottom))
3806 return FALSE;
3808 if (left->has_old_version_top && !info_compare_versions (&left->old_version_top, &right->old_version_top))
3809 return FALSE;
3811 if (left->has_new_version && !info_compare_versions (&left->new_version, &right->new_version))
3812 return FALSE;
3814 return TRUE;
3817 /* LOCKING: assumes all the necessary locks are held */
3818 static void
3819 assembly_binding_info_parsed (MonoAssemblyBindingInfo *info, void *user_data)
3821 MonoAssemblyBindingInfo *info_copy;
3822 GSList *tmp;
3823 MonoAssemblyBindingInfo *info_tmp;
3824 MonoDomain *domain = (MonoDomain*)user_data;
3826 if (!domain)
3827 return;
3829 if (info->has_new_version && mono_assembly_is_problematic_version (info->name, info->new_version.major, info->new_version.minor, info->new_version.build, info->new_version.revision)) {
3830 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Discarding assembly binding to problematic version %s v%d.%d.%d.%d",
3831 info->name, info->new_version.major, info->new_version.minor, info->new_version.build, info->new_version.revision);
3832 return;
3835 for (tmp = domain->assembly_bindings; tmp; tmp = tmp->next) {
3836 info_tmp = (MonoAssemblyBindingInfo *)tmp->data;
3837 if (strcmp (info->name, info_tmp->name) == 0 && info_versions_equal (info, info_tmp))
3838 return;
3841 info_copy = (MonoAssemblyBindingInfo *)mono_mempool_alloc0 (domain->mp, sizeof (MonoAssemblyBindingInfo));
3842 memcpy (info_copy, info, sizeof (MonoAssemblyBindingInfo));
3843 if (info->name)
3844 info_copy->name = mono_mempool_strdup (domain->mp, info->name);
3845 if (info->culture)
3846 info_copy->culture = mono_mempool_strdup (domain->mp, info->culture);
3848 domain->assembly_bindings = g_slist_append_mempool (domain->mp, domain->assembly_bindings, info_copy);
3851 static int
3852 get_version_number (int major, int minor)
3854 return major * 256 + minor;
3857 static inline gboolean
3858 info_major_minor_in_range (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname)
3860 int aname_version_number = get_version_number (aname->major, aname->minor);
3861 if (!info->has_old_version_bottom)
3862 return FALSE;
3864 if (get_version_number (info->old_version_bottom.major, info->old_version_bottom.minor) > aname_version_number)
3865 return FALSE;
3867 if (info->has_old_version_top && get_version_number (info->old_version_top.major, info->old_version_top.minor) < aname_version_number)
3868 return FALSE;
3870 /* This is not the nicest way to do it, but it's a by-product of the way parsing is done */
3871 info->major = aname->major;
3872 info->minor = aname->minor;
3874 return TRUE;
3877 /* LOCKING: Assumes that we are already locked - both loader and domain locks */
3878 static MonoAssemblyBindingInfo*
3879 get_per_domain_assembly_binding_info (MonoDomain *domain, MonoAssemblyName *aname)
3881 MonoAssemblyBindingInfo *info;
3882 GSList *list;
3884 if (!domain->assembly_bindings)
3885 return NULL;
3887 info = NULL;
3888 for (list = domain->assembly_bindings; list; list = list->next) {
3889 info = (MonoAssemblyBindingInfo *)list->data;
3890 if (info && !strcmp (aname->name, info->name) && info_major_minor_in_range (info, aname))
3891 break;
3892 info = NULL;
3895 if (info) {
3896 if (info->name && info->public_key_token [0] && info->has_old_version_bottom &&
3897 info->has_new_version && assembly_binding_maps_name (info, aname))
3898 info->is_valid = TRUE;
3899 else
3900 info->is_valid = FALSE;
3903 return info;
3906 void
3907 mono_domain_parse_assembly_bindings (MonoDomain *domain, int amajor, int aminor, gchar *domain_config_file_name)
3909 if (domain->assembly_bindings_parsed)
3910 return;
3911 mono_domain_lock (domain);
3912 if (!domain->assembly_bindings_parsed) {
3914 gchar *domain_config_file_path = mono_portability_find_file (domain_config_file_name, TRUE);
3916 if (!domain_config_file_path)
3917 domain_config_file_path = domain_config_file_name;
3919 mono_config_parse_assembly_bindings (domain_config_file_path, amajor, aminor, domain, assembly_binding_info_parsed);
3920 domain->assembly_bindings_parsed = TRUE;
3921 if (domain_config_file_name != domain_config_file_path)
3922 g_free (domain_config_file_path);
3925 mono_domain_unlock (domain);
3928 static MonoAssemblyName*
3929 mono_assembly_apply_binding (MonoAssemblyName *aname, MonoAssemblyName *dest_name)
3931 HANDLE_FUNCTION_ENTER ();
3933 ERROR_DECL (error);
3934 MonoAssemblyBindingInfo *info, *info2;
3935 MonoImage *ppimage;
3936 MonoDomain *domain;
3938 if (aname->public_key_token [0] == 0)
3939 goto return_aname;
3941 domain = mono_domain_get ();
3943 mono_assembly_binding_lock ();
3944 info = search_binding_loaded (aname);
3945 mono_assembly_binding_unlock ();
3947 if (!info) {
3948 mono_domain_lock (domain);
3949 info = get_per_domain_assembly_binding_info (domain, aname);
3950 mono_domain_unlock (domain);
3953 if (info) {
3954 if (!check_policy_versions (info, aname))
3955 goto return_aname;
3957 mono_assembly_bind_version (info, aname, dest_name);
3958 goto return_dest_name;
3961 MonoAppDomainSetupHandle setup;
3962 MonoStringHandle configuration_file;
3964 if (domain
3965 && !MONO_HANDLE_IS_NULL (setup = MONO_HANDLE_NEW (MonoAppDomainSetup, domain->setup))
3966 && !MONO_HANDLE_IS_NULL (configuration_file = MONO_HANDLE_NEW_GET (MonoString, setup, configuration_file))) {
3967 char *domain_config_file_name = mono_string_handle_to_utf8 (configuration_file, error);
3968 /* expect this to succeed because mono_domain_set_options_from_config () did
3969 * the same thing when the domain was created. */
3970 mono_error_assert_ok (error);
3971 mono_domain_parse_assembly_bindings (domain, aname->major, aname->minor, domain_config_file_name);
3972 g_free (domain_config_file_name);
3974 mono_domain_lock (domain);
3975 info2 = get_per_domain_assembly_binding_info (domain, aname);
3977 if (info2) {
3978 info = (MonoAssemblyBindingInfo *)g_memdup (info2, sizeof (MonoAssemblyBindingInfo));
3979 info->name = g_strdup (info2->name);
3980 info->culture = g_strdup (info2->culture);
3981 info->domain_id = domain->domain_id;
3984 mono_domain_unlock (domain);
3987 if (!info) {
3988 info = g_new0 (MonoAssemblyBindingInfo, 1);
3989 info->major = aname->major;
3990 info->minor = aname->minor;
3993 if (!info->is_valid) {
3994 ppimage = mono_assembly_load_publisher_policy (aname);
3995 if (ppimage) {
3996 get_publisher_policy_info (ppimage, aname, info);
3997 mono_image_close (ppimage);
4001 /* Define default error value if needed */
4002 if (!info->is_valid) {
4003 info->name = g_strdup (aname->name);
4004 info->culture = g_strdup (aname->culture);
4005 g_strlcpy ((char *)info->public_key_token, (const char *)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
4008 mono_assembly_binding_lock ();
4009 info2 = search_binding_loaded (aname);
4010 if (info2) {
4011 /* This binding was added by another thread
4012 * before us */
4013 mono_assembly_binding_info_free (info);
4014 g_free (info);
4016 info = info2;
4017 } else
4018 loaded_assembly_bindings = g_slist_prepend (loaded_assembly_bindings, info);
4020 mono_assembly_binding_unlock ();
4022 if (!info->is_valid || !check_policy_versions (info, aname))
4023 goto return_aname;
4025 mono_assembly_bind_version (info, aname, dest_name);
4026 goto return_dest_name;
4028 MonoAssemblyName* result;
4030 return_dest_name:
4031 result = dest_name;
4032 goto exit;
4034 return_aname:
4035 result = aname;
4036 goto exit;
4037 exit:
4038 HANDLE_FUNCTION_RETURN_VAL (result);
4042 * mono_assembly_load_from_gac
4044 * \param aname The assembly name object
4046 static MonoAssembly*
4047 mono_assembly_load_from_gac (MonoAssemblyName *aname, gchar *filename, MonoImageOpenStatus *status, MonoBoolean refonly)
4049 MonoAssembly *result = NULL;
4050 gchar *name, *version, *culture, *fullpath, *subpath;
4051 gint32 len;
4052 gchar **paths;
4053 char *pubtok;
4055 if (aname->public_key_token [0] == 0) {
4056 return NULL;
4059 if (strstr (aname->name, ".dll")) {
4060 len = strlen (filename) - 4;
4061 name = (gchar *)g_malloc (len + 1);
4062 memcpy (name, aname->name, len);
4063 name[len] = 0;
4064 } else {
4065 name = g_strdup (aname->name);
4068 if (aname->culture) {
4069 culture = g_utf8_strdown (aname->culture, -1);
4070 } else {
4071 culture = g_strdup ("");
4074 pubtok = g_ascii_strdown ((char*)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
4075 version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major,
4076 aname->minor, aname->build, aname->revision,
4077 culture, pubtok);
4078 g_free (pubtok);
4080 subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL);
4081 g_free (name);
4082 g_free (version);
4083 g_free (culture);
4085 MonoAssemblyOpenRequest req;
4086 mono_assembly_request_prepare (&req.request, sizeof (req), refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT);
4088 if (extra_gac_paths) {
4089 paths = extra_gac_paths;
4090 while (!result && *paths) {
4091 fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", subpath, NULL);
4092 result = mono_assembly_request_open (fullpath, &req, status);
4093 g_free (fullpath);
4094 paths++;
4098 if (result) {
4099 result->in_gac = TRUE;
4100 g_free (subpath);
4101 return result;
4104 fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
4105 "mono", "gac", subpath, NULL);
4106 result = mono_assembly_request_open (fullpath, &req, status);
4107 g_free (fullpath);
4109 if (result)
4110 result->in_gac = TRUE;
4112 g_free (subpath);
4114 return result;
4117 MonoAssembly*
4118 mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *status)
4120 MonoAssemblyName *aname;
4121 MonoAssemblyOpenRequest req;
4122 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
4124 if (corlib) {
4125 /* g_print ("corlib already loaded\n"); */
4126 return corlib;
4129 #ifdef ENABLE_NETCORE
4130 aname = mono_assembly_name_new (MONO_ASSEMBLY_CORLIB_NAME);
4131 corlib = invoke_assembly_preload_hook (aname, NULL);
4132 /* MonoCore preload hook should know how to find it */
4133 /* FIXME: AOT compiler comes here without an installed hook. */
4134 g_assert (corlib);
4135 #else
4136 // A nonstandard preload hook may provide a special mscorlib assembly
4137 aname = mono_assembly_name_new ("mscorlib.dll");
4138 corlib = invoke_assembly_preload_hook (aname, assemblies_path);
4139 mono_assembly_name_free (aname);
4140 g_free (aname);
4141 if (corlib != NULL)
4142 goto return_corlib_and_facades;
4144 // This unusual directory layout can occur if mono is being built and run out of its own source repo
4145 if (assemblies_path) { // Custom assemblies path set via MONO_PATH or mono_set_assemblies_path
4146 corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, &req, status);
4147 if (corlib)
4148 goto return_corlib_and_facades;
4151 /* Normal case: Load corlib from mono/<version> */
4152 char *corlib_file;
4153 corlib_file = g_build_filename ("mono", runtime->framework_version, "mscorlib.dll", NULL);
4154 if (assemblies_path) { // Custom assemblies path
4155 corlib = load_in_path (corlib_file, (const char**)assemblies_path, &req, status);
4156 if (corlib) {
4157 g_free (corlib_file);
4158 goto return_corlib_and_facades;
4161 corlib = load_in_path (corlib_file, (const char**) default_path, &req, status);
4162 g_free (corlib_file);
4164 return_corlib_and_facades:
4165 if (corlib) // FIXME: stop hardcoding 4.5 here
4166 default_path [1] = g_strdup_printf ("%s/Facades", corlib->basedir);
4167 #endif /*!ENABLE_NETCORE*/
4169 return corlib;
4172 static MonoAssembly*
4173 prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly)
4175 ERROR_DECL (refasm_error);
4176 if (candidate && !refonly) {
4177 /* .NET Framework seems to not check for ReferenceAssemblyAttribute on dynamic assemblies */
4178 if (!image_is_dynamic (candidate->image) &&
4179 mono_assembly_has_reference_assembly_attribute (candidate, refasm_error))
4180 candidate = NULL;
4182 mono_error_cleanup (refasm_error);
4183 return candidate;
4186 gboolean
4187 mono_assembly_candidate_predicate_sn_same_name (MonoAssembly *candidate, gpointer ud)
4189 MonoAssemblyName *wanted_name = (MonoAssemblyName*)ud;
4190 MonoAssemblyName *candidate_name = &candidate->aname;
4192 g_assert (wanted_name != NULL);
4193 g_assert (candidate_name != NULL);
4195 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
4196 char * s = mono_stringify_assembly_name (wanted_name);
4197 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: wanted = %s\n", s);
4198 g_free (s);
4199 s = mono_stringify_assembly_name (candidate_name);
4200 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate = %s\n", s);
4201 g_free (s);
4205 /* Wanted name has no token, not strongly named: always matches. */
4206 if (0 == wanted_name->public_key_token [0]) {
4207 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: wanted has no token, returning TRUE\n");
4208 return TRUE;
4211 /* Candidate name has no token, not strongly named: never matches */
4212 if (0 == candidate_name->public_key_token [0]) {
4213 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate has no token, returning FALSE\n");
4214 return FALSE;
4217 return exact_sn_match (wanted_name, candidate_name) ||
4218 framework_assembly_sn_match (wanted_name, candidate_name);
4221 gboolean
4222 exact_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name)
4224 gboolean result = mono_assembly_names_equal (wanted_name, candidate_name);
4226 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate and wanted names %s\n",
4227 result ? "match, returning TRUE" : "don't match, returning FALSE");
4228 return result;
4232 gboolean
4233 framework_assembly_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name)
4235 #ifndef DISABLE_DESKTOP_LOADER
4236 g_assert (wanted_name != NULL);
4237 g_assert (candidate_name != NULL);
4238 const AssemblyVersionMap *vmap = (AssemblyVersionMap *)g_hash_table_lookup (assembly_remapping_table, wanted_name->name);
4239 if (vmap) {
4240 if (!vmap->framework_facade_assembly) {
4241 /* If the wanted name is a framework assembly, it's enough for the name/version/culture to match. If the assembly was remapped, the public key token is likely unrelated. */
4242 gboolean result = mono_assembly_names_equal_flags (wanted_name, candidate_name, MONO_ANAME_EQ_IGNORE_PUBKEY);
4243 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate and wanted names %s (ignoring the public key token)", result ? "match, returning TRUE" : "don't match, returning FALSE");
4244 return result;
4245 } else {
4246 /* For facades, the name and public key token should
4247 * match, but the version doesn't matter as long as the
4248 * candidate is not older. */
4249 gboolean result = mono_assembly_names_equal_flags (wanted_name, candidate_name, MONO_ANAME_EQ_IGNORE_VERSION);
4250 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate and wanted names %s (ignoring version)", result ? "match" : "don't match, returning FALSE");
4251 if (result) {
4252 // compare major of candidate and wanted
4253 int c = assembly_names_compare_versions (candidate_name, wanted_name, 1);
4254 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate major version is %s wanted major version, returning %s\n", c == 0 ? "the same as" : (c < 0 ? "lower than" : "greater than"),
4255 (c >= 0) ? "TRUE" : "FALSE");
4256 return (c >= 0); // don't accept a candidate that's older than wanted.
4257 } else {
4258 return FALSE;
4262 #endif
4263 return FALSE;
4266 static MonoAssembly*
4267 mono_assembly_request_byname_nosearch (MonoAssemblyName *aname,
4268 const MonoAssemblyByNameRequest *req,
4269 MonoImageOpenStatus *status)
4271 MonoAssembly *result;
4272 MonoAssemblyName maped_aname;
4273 MonoAssemblyName maped_name_pp;
4275 aname = mono_assembly_remap_version (aname, &maped_aname);
4277 const gboolean refonly = req->request.asmctx == MONO_ASMCTX_REFONLY;
4279 /* Reflection only assemblies don't get assembly binding */
4280 if (!refonly)
4281 aname = mono_assembly_apply_binding (aname, &maped_name_pp);
4283 result = mono_assembly_loaded_full (aname, refonly);
4284 if (result)
4285 return result;
4287 result = refonly ? invoke_assembly_refonly_preload_hook (aname, assemblies_path) : invoke_assembly_preload_hook (aname, assemblies_path);
4288 if (result) {
4289 result->in_gac = FALSE;
4290 return result;
4293 return mono_assembly_load_full_gac_base_default (aname, req->basedir, req->request.asmctx, status);
4296 /* Like mono_assembly_request_byname_nosearch, but don't ask the preload look (ie,
4297 * the appdomain) to run. Just looks in the gac, the specified base dir or the
4298 * default_path. Does NOT look in the appdomain application base or in the
4299 * MONO_PATH.
4301 MonoAssembly*
4302 mono_assembly_load_full_gac_base_default (MonoAssemblyName *aname,
4303 const char *basedir,
4304 MonoAssemblyContextKind asmctx,
4305 MonoImageOpenStatus *status)
4307 MonoAssembly *result;
4308 char *fullpath, *filename;
4309 int ext_index;
4310 const char *ext;
4311 int len;
4313 /* Currently we retrieve the loaded corlib for reflection
4314 * only requests, like a common reflection only assembly
4316 gboolean name_is_corlib = strcmp (aname->name, MONO_ASSEMBLY_CORLIB_NAME) == 0;
4317 /* Assembly.Load (new AssemblyName ("mscorlib.dll")) (respectively,
4318 * "System.Private.CoreLib.dll" for netcore) is treated the same as
4319 * "mscorlib" (resp "System.Private.CoreLib"). */
4320 name_is_corlib = name_is_corlib || strcmp (aname->name, MONO_ASSEMBLY_CORLIB_NAME ".dll") == 0;
4321 if (name_is_corlib) {
4322 return mono_assembly_load_corlib (mono_get_runtime_info (), status);
4325 MonoAssemblyCandidatePredicate predicate = NULL;
4326 void* predicate_ud = NULL;
4327 #if !defined(DISABLE_DESKTOP_LOADER)
4328 if (G_LIKELY (mono_loader_get_strict_strong_names ())) {
4329 predicate = &mono_assembly_candidate_predicate_sn_same_name;
4330 predicate_ud = aname;
4332 #endif
4334 const gboolean refonly = asmctx == MONO_ASMCTX_REFONLY;
4336 MonoAssemblyOpenRequest req;
4337 mono_assembly_request_prepare (&req.request, sizeof (req), asmctx);
4338 req.request.predicate = predicate;
4339 req.request.predicate_ud = predicate_ud;
4341 len = strlen (aname->name);
4342 for (ext_index = 0; ext_index < 2; ext_index ++) {
4343 ext = ext_index == 0 ? ".dll" : ".exe";
4344 if (len > 4 && (!strcmp (aname->name + len - 4, ".dll") || !strcmp (aname->name + len - 4, ".exe"))) {
4345 filename = g_strdup (aname->name);
4346 /* Don't try appending .dll/.exe if it already has one of those extensions */
4347 ext_index++;
4348 } else {
4349 filename = g_strconcat (aname->name, ext, NULL);
4352 result = mono_assembly_load_from_gac (aname, filename, status, refonly);
4353 if (result) {
4354 g_free (filename);
4355 return result;
4358 if (basedir) {
4359 fullpath = g_build_filename (basedir, filename, NULL);
4360 result = mono_assembly_request_open (fullpath, &req, status);
4361 g_free (fullpath);
4362 if (result) {
4363 result->in_gac = FALSE;
4364 g_free (filename);
4365 return result;
4369 result = load_in_path (filename, (const char**) default_path, &req, status);
4370 if (result)
4371 result->in_gac = FALSE;
4372 g_free (filename);
4373 if (result)
4374 return result;
4377 return result;
4380 MonoAssembly*
4381 mono_assembly_request_byname (MonoAssemblyName *aname, const MonoAssemblyByNameRequest *req, MonoImageOpenStatus *status)
4383 MonoAssembly *result = mono_assembly_request_byname_nosearch (aname, req, status);
4384 const gboolean refonly = req->request.asmctx == MONO_ASMCTX_REFONLY;
4386 if (!result && !req->no_postload_search) {
4387 /* Try a postload search hook */
4388 result = mono_assembly_invoke_search_hook_internal (aname, req->requesting_assembly, refonly, TRUE);
4389 result = prevent_reference_assembly_from_running (result, refonly);
4391 return result;
4395 * mono_assembly_load_full:
4396 * \param aname A MonoAssemblyName with the assembly name to load.
4397 * \param basedir A directory to look up the assembly at.
4398 * \param status a pointer to a MonoImageOpenStatus to return the status of the load operation
4399 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
4401 * Loads the assembly referenced by \p aname, if the value of \p basedir is not NULL, it
4402 * attempts to load the assembly from that directory before probing the standard locations.
4404 * If the assembly is being opened in reflection-only mode (\p refonly set to TRUE) then no
4405 * assembly binding takes place.
4407 * \returns the assembly referenced by \p aname loaded or NULL on error. On error the
4408 * value pointed by \p status is updated with an error code.
4410 MonoAssembly*
4411 mono_assembly_load_full (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status, gboolean refonly)
4413 MonoAssembly *res;
4414 MONO_ENTER_GC_UNSAFE;
4415 MonoAssemblyByNameRequest req;
4416 mono_assembly_request_prepare (&req.request, sizeof (req), refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT);
4417 req.requesting_assembly = NULL;
4418 req.basedir = basedir;
4419 res = mono_assembly_request_byname (aname, &req, status);
4420 MONO_EXIT_GC_UNSAFE;
4421 return res;
4425 * mono_assembly_load:
4426 * \param aname A MonoAssemblyName with the assembly name to load.
4427 * \param basedir A directory to look up the assembly at.
4428 * \param status a pointer to a MonoImageOpenStatus to return the status of the load operation
4430 * Loads the assembly referenced by \p aname, if the value of \p basedir is not NULL, it
4431 * attempts to load the assembly from that directory before probing the standard locations.
4433 * \returns the assembly referenced by \p aname loaded or NULL on error. On error the
4434 * value pointed by \p status is updated with an error code.
4436 MonoAssembly*
4437 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
4439 MonoAssemblyByNameRequest req;
4440 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
4441 req.requesting_assembly = NULL;
4442 req.basedir = basedir;
4443 return mono_assembly_request_byname (aname, &req, status);
4447 * mono_assembly_loaded_full:
4448 * \param aname an assembly to look for.
4449 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
4451 * This is used to determine if the specified assembly has been loaded
4452 * \returns NULL If the given \p aname assembly has not been loaded, or a pointer to
4453 * a \c MonoAssembly that matches the \c MonoAssemblyName specified.
4455 MonoAssembly*
4456 mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly)
4458 MonoAssembly *res;
4459 MonoAssemblyName maped_aname;
4461 aname = mono_assembly_remap_version (aname, &maped_aname);
4463 res = mono_assembly_invoke_search_hook_internal (aname, NULL, refonly, FALSE);
4465 return res;
4469 * mono_assembly_loaded:
4470 * \param aname an assembly to look for.
4472 * This is used to determine if the specified assembly has been loaded
4474 * \returns NULL If the given \p aname assembly has not been loaded, or a pointer to
4475 * a \c MonoAssembly that matches the \c MonoAssemblyName specified.
4477 MonoAssembly*
4478 mono_assembly_loaded (MonoAssemblyName *aname)
4480 MonoAssembly *res;
4481 MONO_ENTER_GC_UNSAFE;
4482 res = mono_assembly_loaded_full (aname, FALSE);
4483 MONO_EXIT_GC_UNSAFE;
4484 return res;
4487 void
4488 mono_assembly_release_gc_roots (MonoAssembly *assembly)
4490 if (assembly == NULL || assembly == REFERENCE_MISSING)
4491 return;
4493 if (assembly_is_dynamic (assembly)) {
4494 int i;
4495 MonoDynamicImage *dynimg = (MonoDynamicImage *)assembly->image;
4496 for (i = 0; i < dynimg->image.module_count; ++i)
4497 mono_dynamic_image_release_gc_roots ((MonoDynamicImage *)dynimg->image.modules [i]);
4498 mono_dynamic_image_release_gc_roots (dynimg);
4503 * Returns whether mono_assembly_close_finish() must be called as
4504 * well. See comment for mono_image_close_except_pools() for why we
4505 * unload in two steps.
4507 gboolean
4508 mono_assembly_close_except_image_pools (MonoAssembly *assembly)
4510 GSList *tmp;
4511 g_return_val_if_fail (assembly != NULL, FALSE);
4513 if (assembly == REFERENCE_MISSING)
4514 return FALSE;
4516 /* Might be 0 already */
4517 if (mono_atomic_dec_i32 (&assembly->ref_count) > 0)
4518 return FALSE;
4520 MONO_PROFILER_RAISE (assembly_unloading, (assembly));
4522 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Unloading assembly %s [%p].", assembly->aname.name, assembly);
4524 mono_debug_close_image (assembly->image);
4526 mono_assemblies_lock ();
4527 loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
4528 mono_assemblies_unlock ();
4530 assembly->image->assembly = NULL;
4532 if (!mono_image_close_except_pools (assembly->image))
4533 assembly->image = NULL;
4535 for (tmp = assembly->friend_assembly_names; tmp; tmp = tmp->next) {
4536 MonoAssemblyName *fname = (MonoAssemblyName *)tmp->data;
4537 mono_assembly_name_free (fname);
4538 g_free (fname);
4540 g_slist_free (assembly->friend_assembly_names);
4541 g_free (assembly->basedir);
4543 MONO_PROFILER_RAISE (assembly_unloaded, (assembly));
4545 return TRUE;
4548 void
4549 mono_assembly_close_finish (MonoAssembly *assembly)
4551 g_assert (assembly && assembly != REFERENCE_MISSING);
4553 if (assembly->image)
4554 mono_image_close_finish (assembly->image);
4556 if (assembly_is_dynamic (assembly)) {
4557 g_free ((char*)assembly->aname.culture);
4558 } else {
4559 g_free (assembly);
4564 * mono_assembly_close:
4565 * \param assembly the assembly to release.
4567 * This method releases a reference to the \p assembly. The assembly is
4568 * only released when all the outstanding references to it are released.
4570 void
4571 mono_assembly_close (MonoAssembly *assembly)
4573 if (mono_assembly_close_except_image_pools (assembly))
4574 mono_assembly_close_finish (assembly);
4578 * mono_assembly_load_module:
4580 MonoImage*
4581 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
4583 ERROR_DECL (error);
4584 MonoImage *result = mono_assembly_load_module_checked (assembly, idx, error);
4585 mono_error_assert_ok (error);
4586 return result;
4589 MONO_API MonoImage*
4590 mono_assembly_load_module_checked (MonoAssembly *assembly, uint32_t idx, MonoError *error)
4592 return mono_image_load_file_for_image_checked (assembly->image, idx, error);
4597 * mono_assembly_foreach:
4598 * \param func function to invoke for each assembly loaded
4599 * \param user_data data passed to the callback
4601 * Invokes the provided \p func callback for each assembly loaded into
4602 * the runtime. The first parameter passed to the callback is the
4603 * \c MonoAssembly*, and the second parameter is the \p user_data.
4605 * This is done for all assemblies loaded in the runtime, not just
4606 * those loaded in the current application domain.
4608 void
4609 mono_assembly_foreach (GFunc func, gpointer user_data)
4611 GList *copy;
4614 * We make a copy of the list to avoid calling the callback inside the
4615 * lock, which could lead to deadlocks.
4617 mono_assemblies_lock ();
4618 copy = g_list_copy (loaded_assemblies);
4619 mono_assemblies_unlock ();
4621 g_list_foreach (loaded_assemblies, func, user_data);
4623 g_list_free (copy);
4627 * mono_assemblies_cleanup:
4629 * Free all resources used by this module.
4631 void
4632 mono_assemblies_cleanup (void)
4634 GSList *l;
4636 mono_os_mutex_destroy (&assemblies_mutex);
4637 mono_os_mutex_destroy (&assembly_binding_mutex);
4639 for (l = loaded_assembly_bindings; l; l = l->next) {
4640 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data;
4642 mono_assembly_binding_info_free (info);
4643 g_free (info);
4645 g_slist_free (loaded_assembly_bindings);
4647 free_assembly_asmctx_from_path_hooks ();
4648 free_assembly_load_hooks ();
4649 free_assembly_search_hooks ();
4650 free_assembly_preload_hooks ();
4653 /*LOCKING takes the assembly_binding lock*/
4654 void
4655 mono_assembly_cleanup_domain_bindings (guint32 domain_id)
4657 GSList **iter;
4659 mono_assembly_binding_lock ();
4660 iter = &loaded_assembly_bindings;
4661 while (*iter) {
4662 GSList *l = *iter;
4663 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data;
4665 if (info->domain_id == domain_id) {
4666 *iter = l->next;
4667 mono_assembly_binding_info_free (info);
4668 g_free (info);
4669 g_slist_free_1 (l);
4670 } else {
4671 iter = &l->next;
4674 mono_assembly_binding_unlock ();
4678 * Holds the assembly of the application, for
4679 * System.Diagnostics.Process::MainModule
4681 static MonoAssembly *main_assembly=NULL;
4684 * mono_assembly_set_main:
4686 void
4687 mono_assembly_set_main (MonoAssembly *assembly)
4689 main_assembly = assembly;
4693 * mono_assembly_get_main:
4695 * Returns: the assembly for the application, the first assembly that is loaded by the VM
4697 MonoAssembly *
4698 mono_assembly_get_main (void)
4700 return (main_assembly);
4704 * mono_assembly_get_image:
4705 * \param assembly The assembly to retrieve the image from
4707 * \returns the \c MonoImage associated with this assembly.
4709 MonoImage*
4710 mono_assembly_get_image (MonoAssembly *assembly)
4712 MonoImage *res;
4713 MONO_ENTER_GC_UNSAFE;
4714 res = mono_assembly_get_image_internal (assembly);
4715 MONO_EXIT_GC_UNSAFE;
4716 return res;
4719 MonoImage*
4720 mono_assembly_get_image_internal (MonoAssembly *assembly)
4722 MONO_REQ_GC_UNSAFE_MODE;
4723 return assembly->image;
4727 * mono_assembly_get_name:
4728 * \param assembly The assembly to retrieve the name from
4730 * The returned name's lifetime is the same as \p assembly's.
4732 * \returns the \c MonoAssemblyName associated with this assembly.
4734 MonoAssemblyName *
4735 mono_assembly_get_name (MonoAssembly *assembly)
4737 MonoAssemblyName *res;
4738 MONO_ENTER_GC_UNSAFE;
4739 res = mono_assembly_get_name_internal (assembly);
4740 MONO_EXIT_GC_UNSAFE;
4741 return res;
4744 MonoAssemblyName *
4745 mono_assembly_get_name_internal (MonoAssembly *assembly)
4747 MONO_REQ_GC_UNSAFE_MODE;
4748 return &assembly->aname;
4752 * mono_register_bundled_assemblies:
4754 void
4755 mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies)
4757 bundles = assemblies;
4760 #define MONO_DECLSEC_FORMAT_10 0x3C
4761 #define MONO_DECLSEC_FORMAT_20 0x2E
4762 #define MONO_DECLSEC_FIELD 0x53
4763 #define MONO_DECLSEC_PROPERTY 0x54
4765 #define SKIP_VISIBILITY_XML_ATTRIBUTE ("\"SkipVerification\"")
4766 #define SKIP_VISIBILITY_ATTRIBUTE_NAME ("System.Security.Permissions.SecurityPermissionAttribute")
4767 #define SKIP_VISIBILITY_ATTRIBUTE_SIZE (sizeof (SKIP_VISIBILITY_ATTRIBUTE_NAME) - 1)
4768 #define SKIP_VISIBILITY_PROPERTY_NAME ("SkipVerification")
4769 #define SKIP_VISIBILITY_PROPERTY_SIZE (sizeof (SKIP_VISIBILITY_PROPERTY_NAME) - 1)
4771 static gboolean
4772 mono_assembly_try_decode_skip_verification_param (const char *p, const char **resp, gboolean *abort_decoding)
4774 int len;
4775 switch (*p++) {
4776 case MONO_DECLSEC_PROPERTY:
4777 break;
4778 case MONO_DECLSEC_FIELD:
4779 default:
4780 *abort_decoding = TRUE;
4781 return FALSE;
4782 break;
4785 if (*p++ != MONO_TYPE_BOOLEAN) {
4786 *abort_decoding = TRUE;
4787 return FALSE;
4790 /* property name length */
4791 len = mono_metadata_decode_value (p, &p);
4793 if (len >= SKIP_VISIBILITY_PROPERTY_SIZE && !memcmp (p, SKIP_VISIBILITY_PROPERTY_NAME, SKIP_VISIBILITY_PROPERTY_SIZE)) {
4794 p += len;
4795 return *p;
4797 p += len + 1;
4799 *resp = p;
4800 return FALSE;
4803 static gboolean
4804 mono_assembly_try_decode_skip_verification (const char *p, const char *endn)
4806 int i, j, num, len, params_len;
4808 if (*p == MONO_DECLSEC_FORMAT_10) {
4809 gsize read, written;
4810 char *res = g_convert (p, endn - p, "UTF-8", "UTF-16LE", &read, &written, NULL);
4811 if (res) {
4812 gboolean found = strstr (res, SKIP_VISIBILITY_XML_ATTRIBUTE) != NULL;
4813 g_free (res);
4814 return found;
4816 return FALSE;
4818 if (*p++ != MONO_DECLSEC_FORMAT_20)
4819 return FALSE;
4821 /* number of encoded permission attributes */
4822 num = mono_metadata_decode_value (p, &p);
4823 for (i = 0; i < num; ++i) {
4824 gboolean is_valid = FALSE;
4825 gboolean abort_decoding = FALSE;
4827 /* attribute name length */
4828 len = mono_metadata_decode_value (p, &p);
4830 /* We don't really need to fully decode the type. Comparing the name is enough */
4831 is_valid = len >= SKIP_VISIBILITY_ATTRIBUTE_SIZE && !memcmp (p, SKIP_VISIBILITY_ATTRIBUTE_NAME, SKIP_VISIBILITY_ATTRIBUTE_SIZE);
4833 p += len;
4835 /*size of the params table*/
4836 params_len = mono_metadata_decode_value (p, &p);
4837 if (is_valid) {
4838 const char *params_end = p + params_len;
4840 /* number of parameters */
4841 len = mono_metadata_decode_value (p, &p);
4843 for (j = 0; j < len; ++j) {
4844 if (mono_assembly_try_decode_skip_verification_param (p, &p, &abort_decoding))
4845 return TRUE;
4846 if (abort_decoding)
4847 break;
4849 p = params_end;
4850 } else {
4851 p += params_len;
4855 return FALSE;
4859 gboolean
4860 mono_assembly_has_skip_verification (MonoAssembly *assembly)
4862 MonoTableInfo *t;
4863 guint32 cols [MONO_DECL_SECURITY_SIZE];
4864 const char *blob;
4865 int i, len;
4867 if (MONO_SECMAN_FLAG_INIT (assembly->skipverification))
4868 return MONO_SECMAN_FLAG_GET_VALUE (assembly->skipverification);
4870 t = &assembly->image->tables [MONO_TABLE_DECLSECURITY];
4872 for (i = 0; i < t->rows; ++i) {
4873 mono_metadata_decode_row (t, i, cols, MONO_DECL_SECURITY_SIZE);
4874 if ((cols [MONO_DECL_SECURITY_PARENT] & MONO_HAS_DECL_SECURITY_MASK) != MONO_HAS_DECL_SECURITY_ASSEMBLY)
4875 continue;
4876 if (cols [MONO_DECL_SECURITY_ACTION] != SECURITY_ACTION_REQMIN)
4877 continue;
4879 blob = mono_metadata_blob_heap (assembly->image, cols [MONO_DECL_SECURITY_PERMISSIONSET]);
4880 len = mono_metadata_decode_blob_size (blob, &blob);
4881 if (!len)
4882 continue;
4884 if (mono_assembly_try_decode_skip_verification (blob, blob + len)) {
4885 MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, TRUE);
4886 return TRUE;
4890 MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, FALSE);
4891 return FALSE;
4894 MonoAssemblyContextKind
4895 mono_asmctx_get_kind (const MonoAssemblyContext *ctx)
4897 return ctx->kind;
4900 static const char *
4901 mono_asmctx_get_name (const MonoAssemblyContext *asmctx)
4903 static const char* names [] = {
4904 "DEFAULT",
4905 "REFONLY",
4906 "LOADFROM",
4907 "INDIVIDIUAL",
4909 g_assert (asmctx->kind >= 0 && asmctx->kind <= MONO_ASMCTX_LAST);
4910 return names [asmctx->kind];