Update dependencies from https://github.com/dotnet/arcade build 20190725.3 (#15836)
[mono-project.git] / mono / metadata / assembly.c
blob16c78151a686c33726f1e79d742f70d021fe8131
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-internals.h>
37 #include <mono/metadata/mono-config-dirs.h>
38 #include <mono/utils/mono-digest.h>
39 #include <mono/utils/mono-logger-internals.h>
40 #include <mono/utils/mono-path.h>
41 #include <mono/metadata/reflection.h>
42 #include <mono/metadata/coree.h>
43 #include <mono/metadata/cil-coff.h>
44 #include <mono/utils/mono-io-portability.h>
45 #include <mono/utils/atomic.h>
46 #include <mono/utils/mono-os-mutex.h>
48 #ifndef HOST_WIN32
49 #include <sys/types.h>
50 #include <unistd.h>
51 #include <sys/stat.h>
52 #endif
54 #ifdef HOST_DARWIN
55 #include <mach-o/dyld.h>
56 #endif
58 /* 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 */
59 typedef struct {
60 const char* assembly_name;
61 guint8 version_set_index;
62 const char* new_assembly_name;
63 gboolean only_lower_versions;
64 gboolean framework_facade_assembly;
65 } AssemblyVersionMap;
67 /* the default search path is empty, the first slot is replaced with the computed value */
68 static char*
69 default_path [] = {
70 NULL,
71 NULL,
72 NULL
75 /* Contains the list of directories to be searched for assemblies (MONO_PATH) */
76 static char **assemblies_path = NULL;
78 #ifndef DISABLE_GAC
79 /* Contains the list of directories that point to auxiliary GACs */
80 static char **extra_gac_paths = NULL;
81 #endif
83 #ifndef DISABLE_DESKTOP_LOADER
85 #define FACADE_ASSEMBLY(str) {str, 0, NULL, FALSE, TRUE}
87 static GHashTable* assembly_remapping_table;
88 /* The list of system assemblies what will be remapped to the running
89 * runtime version.
90 * This list is stored in @assembly_remapping_table during initialization.
91 * Keep it sorted just to make maintenance easier.
93 * The integer number is an index in the MonoRuntimeInfo structure, whose
94 * values can be found in domain.c - supported_runtimes. Look there
95 * to understand what remapping will be made.
97 * .NET version can be found at https://github.com/dotnet/coreclr/blob/master/src/inc/fxretarget.h#L99
100 static const AssemblyVersionMap framework_assemblies [] = {
101 {"Accessibility", 0},
102 {"Commons.Xml.Relaxng", 0},
103 {"CustomMarshalers", 0},
104 {"I18N", 0},
105 {"I18N.CJK", 0},
106 {"I18N.MidEast", 0},
107 {"I18N.Other", 0},
108 {"I18N.Rare", 0},
109 {"I18N.West", 0},
110 {"Microsoft.Build.Engine", 2, NULL, TRUE},
111 {"Microsoft.Build.Framework", 2, NULL, TRUE},
112 {"Microsoft.Build.Tasks", 2, "Microsoft.Build.Tasks.v4.0"},
113 {"Microsoft.Build.Tasks.v3.5", 2, "Microsoft.Build.Tasks.v4.0"},
114 {"Microsoft.Build.Utilities", 2, "Microsoft.Build.Utilities.v4.0"},
115 {"Microsoft.Build.Utilities.v3.5", 2, "Microsoft.Build.Utilities.v4.0"},
116 {"Microsoft.CSharp", 0},
117 {"Microsoft.VisualBasic", 1},
118 {"Microsoft.VisualC", 1},
119 FACADE_ASSEMBLY ("Microsoft.Win32.Primitives"),
120 FACADE_ASSEMBLY ("Microsoft.Win32.Registry"),
121 FACADE_ASSEMBLY ("Microsoft.Win32.Registry.AccessControl"),
122 {"Mono.Cairo", 0},
123 {"Mono.CompilerServices.SymbolWriter", 0},
124 {"Mono.Data", 0},
125 {"Mono.Data.SybaseClient", 0},
126 {"Mono.Data.Tds", 0},
127 {"Mono.Data.TdsClient", 0},
128 {"Mono.GetOptions", 0},
129 {"Mono.Http", 0},
130 {"Mono.Posix", 0},
131 {"Mono.Security", 0},
132 {"Mono.Security.Win32", 0},
133 {"Mono.Xml.Ext", 0},
134 {"Novell.Directory.Ldap", 0},
135 {"PEAPI", 0},
136 {"System", 0},
137 FACADE_ASSEMBLY ("System.AppContext"),
138 FACADE_ASSEMBLY ("System.Buffers"),
139 FACADE_ASSEMBLY ("System.Collections"),
140 FACADE_ASSEMBLY ("System.Collections.Concurrent"),
141 FACADE_ASSEMBLY ("System.Collections.NonGeneric"),
142 FACADE_ASSEMBLY ("System.Collections.Specialized"),
143 FACADE_ASSEMBLY ("System.ComponentModel"),
144 FACADE_ASSEMBLY ("System.ComponentModel.Annotations"),
145 {"System.ComponentModel.Composition", 2},
146 {"System.ComponentModel.DataAnnotations", 2},
147 FACADE_ASSEMBLY ("System.ComponentModel.EventBasedAsync"),
148 FACADE_ASSEMBLY ("System.ComponentModel.Primitives"),
149 FACADE_ASSEMBLY ("System.ComponentModel.TypeConverter"),
150 {"System.Configuration", 0},
151 {"System.Configuration.Install", 0},
152 FACADE_ASSEMBLY ("System.Console"),
153 {"System.Core", 2},
154 {"System.Data", 0},
155 FACADE_ASSEMBLY ("System.Data.Common"),
156 {"System.Data.DataSetExtensions", 0},
157 {"System.Data.Entity", 0},
158 {"System.Data.Linq", 2},
159 {"System.Data.OracleClient", 0},
160 {"System.Data.Services", 2},
161 {"System.Data.Services.Client", 2},
162 FACADE_ASSEMBLY ("System.Data.SqlClient"),
163 {"System.Data.SqlXml", 0},
164 {"System.Deployment", 0},
165 {"System.Design", 0},
166 FACADE_ASSEMBLY ("System.Diagnostics.Contracts"),
167 FACADE_ASSEMBLY ("System.Diagnostics.Debug"),
168 FACADE_ASSEMBLY ("System.Diagnostics.FileVersionInfo"),
169 FACADE_ASSEMBLY ("System.Diagnostics.Process"),
170 FACADE_ASSEMBLY ("System.Diagnostics.StackTrace"),
171 FACADE_ASSEMBLY ("System.Diagnostics.TextWriterTraceListener"),
172 FACADE_ASSEMBLY ("System.Diagnostics.Tools"),
173 FACADE_ASSEMBLY ("System.Diagnostics.TraceEvent"),
174 FACADE_ASSEMBLY ("System.Diagnostics.TraceSource"),
175 FACADE_ASSEMBLY ("System.Diagnostics.Tracing"),
176 {"System.DirectoryServices", 0},
177 {"System.DirectoryServices.Protocols", 0},
178 {"System.Drawing", 0},
179 FACADE_ASSEMBLY ("System.Drawing.Common"),
180 {"System.Drawing.Design", 0},
181 FACADE_ASSEMBLY ("System.Drawing.Primitives"),
182 {"System.Dynamic", 0},
183 FACADE_ASSEMBLY ("System.Dynamic.Runtime"),
184 {"System.EnterpriseServices", 0},
185 FACADE_ASSEMBLY ("System.Globalization"),
186 FACADE_ASSEMBLY ("System.Globalization.Calendars"),
187 FACADE_ASSEMBLY ("System.Globalization.Extensions"),
188 {"System.IdentityModel", 3},
189 {"System.IdentityModel.Selectors", 3},
190 FACADE_ASSEMBLY ("System.IO"),
191 {"System.IO.Compression", 2},
192 {"System.IO.Compression.FileSystem", 0},
193 FACADE_ASSEMBLY ("System.IO.Compression.ZipFile"),
194 FACADE_ASSEMBLY ("System.IO.FileSystem"),
195 FACADE_ASSEMBLY ("System.IO.FileSystem.AccessControl"),
196 FACADE_ASSEMBLY ("System.IO.FileSystem.DriveInfo"),
197 FACADE_ASSEMBLY ("System.IO.FileSystem.Primitives"),
198 FACADE_ASSEMBLY ("System.IO.FileSystem.Watcher"),
199 FACADE_ASSEMBLY ("System.IO.IsolatedStorage"),
200 FACADE_ASSEMBLY ("System.IO.MemoryMappedFiles"),
201 FACADE_ASSEMBLY ("System.IO.Packaging"),
202 FACADE_ASSEMBLY ("System.IO.Pipes"),
203 FACADE_ASSEMBLY ("System.IO.UnmanagedMemoryStream"),
204 FACADE_ASSEMBLY ("System.Linq"),
205 FACADE_ASSEMBLY ("System.Linq.Expressions"),
206 FACADE_ASSEMBLY ("System.Linq.Parallel"),
207 FACADE_ASSEMBLY ("System.Linq.Queryable"),
208 {"System.Management", 0},
209 FACADE_ASSEMBLY ("System.Memory"),
210 {"System.Messaging", 0},
211 {"System.Net", 2},
212 FACADE_ASSEMBLY ("System.Net.AuthenticationManager"),
213 FACADE_ASSEMBLY ("System.Net.Cache"),
214 {"System.Net.Http", 4},
215 {"System.Net.Http.Rtc", 0},
216 {"System.Net.Http.WebRequest", 0},
217 FACADE_ASSEMBLY ("System.Net.HttpListener"),
218 FACADE_ASSEMBLY ("System.Net.Mail"),
219 FACADE_ASSEMBLY ("System.Net.NameResolution"),
220 FACADE_ASSEMBLY ("System.Net.NetworkInformation"),
221 FACADE_ASSEMBLY ("System.Net.Ping"),
222 FACADE_ASSEMBLY ("System.Net.Primitives"),
223 FACADE_ASSEMBLY ("System.Net.Requests"),
224 FACADE_ASSEMBLY ("System.Net.Security"),
225 FACADE_ASSEMBLY ("System.Net.ServicePoint"),
226 FACADE_ASSEMBLY ("System.Net.Sockets"),
227 FACADE_ASSEMBLY ("System.Net.Utilities"),
228 FACADE_ASSEMBLY ("System.Net.WebHeaderCollection"),
229 FACADE_ASSEMBLY ("System.Net.WebSockets"),
230 FACADE_ASSEMBLY ("System.Net.WebSockets.Client"),
231 {"System.Numerics", 3},
232 {"System.Numerics.Vectors", 3},
233 FACADE_ASSEMBLY ("System.ObjectModel"),
234 FACADE_ASSEMBLY ("System.Reflection"),
235 {"System.Reflection.Context", 0},
236 FACADE_ASSEMBLY ("System.Reflection.DispatchProxy"),
237 FACADE_ASSEMBLY ("System.Reflection.Emit"),
238 FACADE_ASSEMBLY ("System.Reflection.Emit.ILGeneration"),
239 FACADE_ASSEMBLY ("System.Reflection.Emit.Lightweight"),
240 FACADE_ASSEMBLY ("System.Reflection.Extensions"),
241 FACADE_ASSEMBLY ("System.Reflection.Primitives"),
242 FACADE_ASSEMBLY ("System.Reflection.TypeExtensions"),
243 FACADE_ASSEMBLY ("System.Resources.Reader"),
244 FACADE_ASSEMBLY ("System.Resources.ReaderWriter"),
245 FACADE_ASSEMBLY ("System.Resources.ResourceManager"),
246 FACADE_ASSEMBLY ("System.Resources.Writer"),
247 FACADE_ASSEMBLY ("System.Runtime"),
248 {"System.Runtime.Caching", 0},
249 FACADE_ASSEMBLY ("System.Runtime.CompilerServices.VisualC"),
250 {"System.Runtime.DurableInstancing", 0},
251 FACADE_ASSEMBLY ("System.Runtime.Extensions"),
252 FACADE_ASSEMBLY ("System.Runtime.Handles"),
253 FACADE_ASSEMBLY ("System.Runtime.InteropServices"),
254 FACADE_ASSEMBLY ("System.Runtime.InteropServices.RuntimeInformation"),
255 FACADE_ASSEMBLY ("System.Runtime.InteropServices.WindowsRuntime"),
256 FACADE_ASSEMBLY ("System.Runtime.Loader"),
257 FACADE_ASSEMBLY ("System.Runtime.Numerics"),
258 {"System.Runtime.Remoting", 0},
259 {"System.Runtime.Serialization", 3},
260 FACADE_ASSEMBLY ("System.Runtime.Serialization.Formatters"),
261 {"System.Runtime.Serialization.Formatters.Soap", 0},
262 FACADE_ASSEMBLY ("System.Runtime.Serialization.Json"),
263 FACADE_ASSEMBLY ("System.Runtime.Serialization.Primitives"),
264 FACADE_ASSEMBLY ("System.Runtime.Serialization.Xml"),
265 {"System.Security", 0},
266 FACADE_ASSEMBLY ("System.Security.AccessControl"),
267 FACADE_ASSEMBLY ("System.Security.Claims"),
268 FACADE_ASSEMBLY ("System.Security.Cryptography.Algorithms"),
269 FACADE_ASSEMBLY ("System.Security.Cryptography.Cng"),
270 FACADE_ASSEMBLY ("System.Security.Cryptography.Csp"),
271 FACADE_ASSEMBLY ("System.Security.Cryptography.DeriveBytes"),
272 FACADE_ASSEMBLY ("System.Security.Cryptography.Encoding"),
273 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption"),
274 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.Aes"),
275 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.ECDiffieHellman"),
276 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.ECDsa"),
277 FACADE_ASSEMBLY ("System.Security.Cryptography.Hashing"),
278 FACADE_ASSEMBLY ("System.Security.Cryptography.Hashing.Algorithms"),
279 FACADE_ASSEMBLY ("System.Security.Cryptography.OpenSsl"),
280 FACADE_ASSEMBLY ("System.Security.Cryptography.Pkcs"),
281 FACADE_ASSEMBLY ("System.Security.Cryptography.Primitives"),
282 FACADE_ASSEMBLY ("System.Security.Cryptography.ProtectedData"),
283 FACADE_ASSEMBLY ("System.Security.Cryptography.RSA"),
284 FACADE_ASSEMBLY ("System.Security.Cryptography.RandomNumberGenerator"),
285 FACADE_ASSEMBLY ("System.Security.Cryptography.X509Certificates"),
286 FACADE_ASSEMBLY ("System.Security.Principal"),
287 FACADE_ASSEMBLY ("System.Security.Principal.Windows"),
288 FACADE_ASSEMBLY ("System.Security.SecureString"),
289 {"System.ServiceModel", 3},
290 {"System.ServiceModel.Activation", 0},
291 {"System.ServiceModel.Discovery", 0},
292 FACADE_ASSEMBLY ("System.ServiceModel.Duplex"),
293 FACADE_ASSEMBLY ("System.ServiceModel.Http"),
294 FACADE_ASSEMBLY ("System.ServiceModel.NetTcp"),
295 FACADE_ASSEMBLY ("System.ServiceModel.Primitives"),
296 {"System.ServiceModel.Routing", 0},
297 FACADE_ASSEMBLY ("System.ServiceModel.Security"),
298 {"System.ServiceModel.Web", 2},
299 {"System.ServiceProcess", 0},
300 FACADE_ASSEMBLY ("System.ServiceProcess.ServiceController"),
301 FACADE_ASSEMBLY ("System.Text.Encoding"),
302 FACADE_ASSEMBLY ("System.Text.Encoding.CodePages"),
303 FACADE_ASSEMBLY ("System.Text.Encoding.Extensions"),
304 FACADE_ASSEMBLY ("System.Text.RegularExpressions"),
305 FACADE_ASSEMBLY ("System.Threading"),
306 FACADE_ASSEMBLY ("System.Threading.AccessControl"),
307 FACADE_ASSEMBLY ("System.Threading.Overlapped"),
308 FACADE_ASSEMBLY ("System.Threading.Tasks"),
309 {"System.Threading.Tasks.Dataflow", 0, NULL, TRUE},
310 FACADE_ASSEMBLY ("System.Threading.Tasks.Extensions"),
311 FACADE_ASSEMBLY ("System.Threading.Tasks.Parallel"),
312 FACADE_ASSEMBLY ("System.Threading.Thread"),
313 FACADE_ASSEMBLY ("System.Threading.ThreadPool"),
314 FACADE_ASSEMBLY ("System.Threading.Timer"),
315 {"System.Transactions", 0},
316 FACADE_ASSEMBLY ("System.ValueTuple"),
317 {"System.Web", 0},
318 {"System.Web.Abstractions", 2},
319 {"System.Web.ApplicationServices", 0},
320 {"System.Web.DynamicData", 2},
321 {"System.Web.Extensions", 2},
322 {"System.Web.Extensions.Design", 0},
323 {"System.Web.Mobile", 0},
324 {"System.Web.RegularExpressions", 0},
325 {"System.Web.Routing", 2},
326 {"System.Web.Services", 0},
327 {"System.Windows", 0},
328 {"System.Windows.Forms", 0},
329 {"System.Windows.Forms.DataVisualization", 0},
330 {"System.Workflow.Activities", 0},
331 {"System.Workflow.ComponentModel", 0},
332 {"System.Workflow.Runtime", 0},
333 {"System.Xaml", 0},
334 {"System.Xml", 0},
335 {"System.Xml.Linq", 2},
336 FACADE_ASSEMBLY ("System.Xml.ReaderWriter"),
337 {"System.Xml.Serialization", 0},
338 FACADE_ASSEMBLY ("System.Xml.XDocument"),
339 FACADE_ASSEMBLY ("System.Xml.XPath"),
340 FACADE_ASSEMBLY ("System.Xml.XPath.XmlDocument"),
341 FACADE_ASSEMBLY ("System.Xml.XPath.XDocument"),
342 FACADE_ASSEMBLY ("System.Xml.XmlDocument"),
343 FACADE_ASSEMBLY ("System.Xml.XmlSerializer"),
344 FACADE_ASSEMBLY ("System.Xml.Xsl.Primitives"),
345 {"WindowsBase", 3},
346 {"cscompmgd", 0},
347 {"mscorlib", 0},
348 FACADE_ASSEMBLY ("netstandard"),
350 #endif
352 /* keeps track of loaded assemblies, excluding dynamic ones */
353 static GList *loaded_assemblies = NULL;
354 static MonoAssembly *corlib;
356 static char* unquote (const char *str);
358 /* This protects loaded_assemblies and image->references */
359 #define mono_assemblies_lock() mono_os_mutex_lock (&assemblies_mutex)
360 #define mono_assemblies_unlock() mono_os_mutex_unlock (&assemblies_mutex)
361 static mono_mutex_t assemblies_mutex;
363 /* If defined, points to the bundled assembly information */
364 static const MonoBundledAssembly **bundles;
366 static mono_mutex_t assembly_binding_mutex;
368 /* Loaded assembly binding info */
369 static GSList *loaded_assembly_bindings = NULL;
371 /* Class lazy loading functions */
372 static GENERATE_TRY_GET_CLASS_WITH_CACHE (internals_visible, "System.Runtime.CompilerServices", "InternalsVisibleToAttribute")
373 static MonoAssembly*
374 mono_assembly_invoke_search_hook_internal (MonoAssemblyLoadContext *alc, MonoAssembly *requesting, MonoAssemblyName *aname, gboolean refonly, gboolean postload);
375 static MonoAssembly*
376 mono_assembly_request_byname_nosearch (MonoAssemblyName *aname, const MonoAssemblyByNameRequest *req, MonoImageOpenStatus *status);
377 static MonoAssembly*
378 mono_assembly_load_full_gac_base_default (MonoAssemblyName *aname, const char *basedir, MonoAssemblyLoadContext *alc, MonoAssemblyContextKind asmctx, MonoImageOpenStatus *status);
379 static MonoAssembly*
380 chain_redirections_loadfrom (MonoImage *image, MonoImageOpenStatus *status);
381 static MonoAssembly*
382 mono_problematic_image_reprobe (MonoImage *image, MonoImageOpenStatus *status);
384 static MonoBoolean
385 mono_assembly_is_in_gac (const gchar *filanem);
386 static MonoAssemblyName*
387 mono_assembly_apply_binding (MonoAssemblyName *aname, MonoAssemblyName *dest_name);
389 static MonoAssembly*
390 prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly);
392 /* Assembly name matching */
393 static gboolean
394 exact_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name);
395 static gboolean
396 framework_assembly_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name);
398 static const char *
399 mono_asmctx_get_name (const MonoAssemblyContext *asmctx);
401 static gboolean
402 assembly_loadfrom_asmctx_from_path (const char *filename, MonoAssembly *requesting_assembly, gpointer user_data, MonoAssemblyContextKind *out_asmctx);
404 static gchar*
405 encode_public_tok (const guchar *token, gint32 len)
407 const static gchar allowed [] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
408 gchar *res;
409 int i;
411 res = (gchar *)g_malloc (len * 2 + 1);
412 for (i = 0; i < len; i++) {
413 res [i * 2] = allowed [token [i] >> 4];
414 res [i * 2 + 1] = allowed [token [i] & 0xF];
416 res [len * 2] = 0;
417 return res;
421 * mono_public_tokens_are_equal:
422 * \param pubt1 first public key token
423 * \param pubt2 second public key token
425 * Compare two public key tokens and return TRUE is they are equal and FALSE
426 * otherwise.
428 gboolean
429 mono_public_tokens_are_equal (const unsigned char *pubt1, const unsigned char *pubt2)
431 return g_ascii_strncasecmp ((const char*) pubt1, (const char*) pubt2, 16) == 0;
435 * mono_set_assemblies_path:
436 * \param path list of paths that contain directories where Mono will look for assemblies
438 * Use this method to override the standard assembly lookup system and
439 * override any assemblies coming from the GAC. This is the method
440 * that supports the \c MONO_PATH variable.
442 * Notice that \c MONO_PATH and this method are really a very bad idea as
443 * it prevents the GAC from working and it prevents the standard
444 * resolution mechanisms from working. Nonetheless, for some debugging
445 * situations and bootstrapping setups, this is useful to have.
447 void
448 mono_set_assemblies_path (const char* path)
450 char **splitted, **dest;
452 splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
453 if (assemblies_path)
454 g_strfreev (assemblies_path);
455 assemblies_path = dest = splitted;
456 while (*splitted) {
457 char *tmp = *splitted;
458 if (*tmp)
459 *dest++ = mono_path_canonicalize (tmp);
460 g_free (tmp);
461 splitted++;
463 *dest = *splitted;
465 if (g_hasenv ("MONO_DEBUG"))
466 return;
468 splitted = assemblies_path;
469 while (*splitted) {
470 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
471 g_warning ("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *splitted);
473 splitted++;
477 static void
478 check_path_env (void)
480 if (assemblies_path != NULL)
481 return;
483 char* path = g_getenv ("MONO_PATH");
484 if (!path)
485 return;
487 mono_set_assemblies_path(path);
488 g_free (path);
491 #ifndef DISABLE_GAC
492 static void
493 check_extra_gac_path_env (void)
495 char *path;
496 char **splitted, **dest;
498 path = g_getenv ("MONO_GAC_PREFIX");
499 if (!path)
500 return;
502 splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
503 g_free (path);
505 if (extra_gac_paths)
506 g_strfreev (extra_gac_paths);
507 extra_gac_paths = dest = splitted;
508 while (*splitted){
509 if (**splitted)
510 *dest++ = *splitted;
511 splitted++;
513 *dest = *splitted;
515 if (!g_hasenv ("MONO_DEBUG"))
516 return;
518 while (*splitted) {
519 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
520 g_warning ("'%s' in MONO_GAC_PREFIX doesn't exist or has wrong permissions.", *splitted);
522 splitted++;
525 #endif /* DISABLE_GAC */
527 static gboolean
528 assembly_binding_maps_name (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname)
530 if (!info || !info->name)
531 return FALSE;
533 if (strcmp (info->name, aname->name))
534 return FALSE;
536 if (info->major != aname->major || info->minor != aname->minor)
537 return FALSE;
539 if ((info->culture != NULL && info->culture [0]) != (aname->culture != NULL && aname->culture [0]))
540 return FALSE;
542 if (info->culture && aname->culture && strcmp (info->culture, aname->culture))
543 return FALSE;
545 if (!mono_public_tokens_are_equal (info->public_key_token, aname->public_key_token))
546 return FALSE;
548 return TRUE;
551 static void
552 mono_assembly_binding_info_free (MonoAssemblyBindingInfo *info)
554 if (!info)
555 return;
557 g_free (info->name);
558 g_free (info->culture);
561 static void
562 get_publisher_policy_info (MonoImage *image, MonoAssemblyName *aname, MonoAssemblyBindingInfo *binding_info)
564 MonoTableInfo *t;
565 guint32 cols [MONO_MANIFEST_SIZE];
566 const gchar *filename;
567 gchar *subpath, *fullpath;
569 t = &image->tables [MONO_TABLE_MANIFESTRESOURCE];
570 /* MS Impl. accepts policy assemblies with more than
571 * one manifest resource, and only takes the first one */
572 if (t->rows < 1) {
573 binding_info->is_valid = FALSE;
574 return;
577 mono_metadata_decode_row (t, 0, cols, MONO_MANIFEST_SIZE);
578 if ((cols [MONO_MANIFEST_IMPLEMENTATION] & MONO_IMPLEMENTATION_MASK) != MONO_IMPLEMENTATION_FILE) {
579 binding_info->is_valid = FALSE;
580 return;
583 filename = mono_metadata_string_heap (image, cols [MONO_MANIFEST_NAME]);
584 g_assert (filename != NULL);
586 subpath = g_path_get_dirname (image->name);
587 fullpath = g_build_path (G_DIR_SEPARATOR_S, subpath, filename, NULL);
588 mono_config_parse_publisher_policy (fullpath, binding_info);
589 g_free (subpath);
590 g_free (fullpath);
592 /* Define the optional elements/attributes before checking */
593 if (!binding_info->culture)
594 binding_info->culture = g_strdup ("");
596 /* Check that the most important elements/attributes exist */
597 if (!binding_info->name || !binding_info->public_key_token [0] || !binding_info->has_old_version_bottom ||
598 !binding_info->has_new_version || !assembly_binding_maps_name (binding_info, aname)) {
599 mono_assembly_binding_info_free (binding_info);
600 binding_info->is_valid = FALSE;
601 return;
604 binding_info->is_valid = TRUE;
607 static int
608 compare_versions (AssemblyVersionSet *v, MonoAssemblyName *aname)
610 if (v->major > aname->major)
611 return 1;
612 else if (v->major < aname->major)
613 return -1;
615 if (v->minor > aname->minor)
616 return 1;
617 else if (v->minor < aname->minor)
618 return -1;
620 if (v->build > aname->build)
621 return 1;
622 else if (v->build < aname->build)
623 return -1;
625 if (v->revision > aname->revision)
626 return 1;
627 else if (v->revision < aname->revision)
628 return -1;
630 return 0;
633 static gboolean
634 check_policy_versions (MonoAssemblyBindingInfo *info, MonoAssemblyName *name)
636 if (!info->is_valid)
637 return FALSE;
639 /* If has_old_version_top doesn't exist, we don't have an interval */
640 if (!info->has_old_version_top) {
641 if (compare_versions (&info->old_version_bottom, name) == 0)
642 return TRUE;
644 return FALSE;
647 /* Check that the version defined by name is valid for the interval */
648 if (compare_versions (&info->old_version_top, name) < 0)
649 return FALSE;
651 /* We should be greater or equal than the small version */
652 if (compare_versions (&info->old_version_bottom, name) > 0)
653 return FALSE;
655 return TRUE;
659 * mono_assembly_names_equal:
660 * \param l first assembly
661 * \param r second assembly.
663 * Compares two \c MonoAssemblyName instances and returns whether they are equal.
665 * This compares the names, the cultures, the release version and their
666 * public tokens.
668 * \returns TRUE if both assembly names are equal.
670 gboolean
671 mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r)
673 return mono_assembly_names_equal_flags (l, r, MONO_ANAME_EQ_NONE);
677 * mono_assembly_names_equal_flags:
678 * \param l first assembly name
679 * \param r second assembly name
680 * \param flags flags that affect what is compared.
682 * Compares two \c MonoAssemblyName instances and returns whether they are equal.
684 * This compares the simple names and cultures and optionally the versions and
685 * public key tokens, depending on the \c flags.
687 * \returns TRUE if both assembly names are equal.
689 gboolean
690 mono_assembly_names_equal_flags (MonoAssemblyName *l, MonoAssemblyName *r, MonoAssemblyNameEqFlags flags)
692 g_assert (l != NULL);
693 g_assert (r != NULL);
695 if (!l->name || !r->name)
696 return FALSE;
698 if ((flags & MONO_ANAME_EQ_IGNORE_CASE) != 0 && g_strcasecmp (l->name, r->name))
699 return FALSE;
701 if ((flags & MONO_ANAME_EQ_IGNORE_CASE) == 0 && strcmp (l->name, r->name))
702 return FALSE;
704 if (l->culture && r->culture && strcmp (l->culture, r->culture))
705 return FALSE;
707 if ((l->major != r->major || l->minor != r->minor ||
708 l->build != r->build || l->revision != r->revision) &&
709 (flags & MONO_ANAME_EQ_IGNORE_VERSION) == 0)
710 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)))
711 return FALSE;
713 if (!l->public_key_token [0] || !r->public_key_token [0] || (flags & MONO_ANAME_EQ_IGNORE_PUBKEY) != 0)
714 return TRUE;
716 if (!mono_public_tokens_are_equal (l->public_key_token, r->public_key_token))
717 return FALSE;
719 return TRUE;
723 * assembly_names_compare_versions:
724 * \param l left assembly name
725 * \param r right assembly name
726 * \param maxcomps how many version components to compare, or -1 to compare all.
728 * \returns a negative if \p l is a lower version than \p r; a positive value
729 * if \p r is a lower version than \p l, or zero if \p l and \p r are equal
730 * versions (comparing upto \p maxcomps components).
732 * Components are \c major, \c minor, \c revision, and \c build. \p maxcomps 1 means just compare
733 * majors. 2 means majors then minors. etc.
735 static int
736 assembly_names_compare_versions (MonoAssemblyName *l, MonoAssemblyName *r, int maxcomps)
738 int i = 0;
739 if (maxcomps < 0) maxcomps = 4;
740 #define CMP(field) do { \
741 if (l-> field < r-> field && i < maxcomps) return -1; \
742 if (l-> field > r-> field && i < maxcomps) return 1; \
743 } while (0)
744 CMP (major);
745 ++i;
746 CMP (minor);
747 ++i;
748 CMP (revision);
749 ++i;
750 CMP (build);
751 #undef CMP
752 return 0;
756 * mono_assembly_request_prepare:
757 * \param req the request to be initialized
758 * \param req_size the size of the request structure
759 * \param asmctx the assembly load context kind
761 * Initialize an assembly loader request. The passed structure \p req must be
762 * of size \p req_size. Its state will be reset and the assembly context kind will be prefilled with \p asmctx.
764 void
765 mono_assembly_request_prepare (MonoAssemblyLoadRequest *req, size_t req_size, MonoAssemblyContextKind asmctx)
767 memset (req, 0, req_size);
768 req->asmctx = asmctx;
771 static MonoAssembly *
772 load_in_path (const char *basename, const char** search_path, const MonoAssemblyOpenRequest *req, MonoImageOpenStatus *status)
774 int i;
775 char *fullpath;
776 MonoAssembly *result;
778 for (i = 0; search_path [i]; ++i) {
779 fullpath = g_build_filename (search_path [i], basename, NULL);
780 result = mono_assembly_request_open (fullpath, req, status);
781 g_free (fullpath);
782 if (result)
783 return result;
785 return NULL;
789 * mono_assembly_setrootdir:
790 * \param root_dir The pathname of the root directory where we will locate assemblies
792 * This routine sets the internal default root directory for looking up
793 * assemblies.
795 * This is used by Windows installations to compute dynamically the
796 * place where the Mono assemblies are located.
799 void
800 mono_assembly_setrootdir (const char *root_dir)
803 * Override the MONO_ASSEMBLIES directory configured at compile time.
805 if (default_path [0])
806 g_free (default_path [0]);
807 default_path [0] = g_strdup (root_dir);
811 * mono_assembly_getrootdir:
813 * Obtains the root directory used for looking up assemblies.
815 * Returns: a string with the directory, this string should not be freed.
817 G_CONST_RETURN gchar *
818 mono_assembly_getrootdir (void)
820 return default_path [0];
824 * mono_native_getrootdir:
826 * Obtains the root directory used for looking up native libs (.so, .dylib).
828 * Returns: a string with the directory, this string should be freed by
829 * the caller.
831 gchar *
832 mono_native_getrootdir (void)
834 gchar* fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), mono_config_get_reloc_lib_dir(), NULL);
835 return fullpath;
839 * mono_set_dirs:
840 * \param assembly_dir the base directory for assemblies
841 * \param config_dir the base directory for configuration files
843 * This routine is used internally and by developers embedding
844 * the runtime into their own applications.
846 * There are a number of cases to consider: Mono as a system-installed
847 * package that is available on the location preconfigured or Mono in
848 * a relocated location.
850 * If you are using a system-installed Mono, you can pass NULL
851 * to both parameters. If you are not, you should compute both
852 * directory values and call this routine.
854 * The values for a given PREFIX are:
856 * assembly_dir: PREFIX/lib
857 * config_dir: PREFIX/etc
859 * Notice that embedders that use Mono in a relocated way must
860 * compute the location at runtime, as they will be in control
861 * of where Mono is installed.
863 void
864 mono_set_dirs (const char *assembly_dir, const char *config_dir)
866 if (assembly_dir == NULL)
867 assembly_dir = mono_config_get_assemblies_dir ();
868 if (config_dir == NULL)
869 config_dir = mono_config_get_cfg_dir ();
870 mono_assembly_setrootdir (assembly_dir);
871 mono_set_config_dir (config_dir);
874 #ifndef HOST_WIN32
876 static char *
877 compute_base (char *path)
879 char *p = strrchr (path, '/');
880 if (p == NULL)
881 return NULL;
883 /* Not a well known Mono executable, we are embedded, cant guess the base */
884 if (strcmp (p, "/mono") && strcmp (p, "/mono-boehm") && strcmp (p, "/mono-sgen") && strcmp (p, "/pedump") && strcmp (p, "/monodis"))
885 return NULL;
887 *p = 0;
888 p = strrchr (path, '/');
889 if (p == NULL)
890 return NULL;
892 if (strcmp (p, "/bin") != 0)
893 return NULL;
894 *p = 0;
895 return path;
898 static void
899 fallback (void)
901 mono_set_dirs (mono_config_get_assemblies_dir (), mono_config_get_cfg_dir ());
904 static G_GNUC_UNUSED void
905 set_dirs (char *exe)
907 char *base;
908 char *config, *lib, *mono;
909 struct stat buf;
910 const char *bindir;
913 * Only /usr prefix is treated specially
915 bindir = mono_config_get_bin_dir ();
916 g_assert (bindir);
917 if (strncmp (exe, bindir, strlen (bindir)) == 0 || (base = compute_base (exe)) == NULL){
918 fallback ();
919 return;
922 config = g_build_filename (base, "etc", NULL);
923 lib = g_build_filename (base, "lib", NULL);
924 mono = g_build_filename (lib, "mono/4.5", NULL); // FIXME: stop hardcoding 4.5 here
925 if (stat (mono, &buf) == -1)
926 fallback ();
927 else {
928 mono_set_dirs (lib, config);
931 g_free (config);
932 g_free (lib);
933 g_free (mono);
936 #endif /* HOST_WIN32 */
939 * mono_set_rootdir:
941 * Registers the root directory for the Mono runtime, for Linux and Solaris 10,
942 * this auto-detects the prefix where Mono was installed.
944 void
945 mono_set_rootdir (void)
947 #if defined(HOST_WIN32) || (defined(HOST_DARWIN) && !defined(TARGET_ARM))
948 gchar *bindir, *installdir, *root, *name, *resolvedname, *config;
950 #ifdef HOST_WIN32
951 name = mono_get_module_file_name ((HMODULE) &__ImageBase);
952 #else
955 * _NSGetExecutablePath may return -1 to indicate buf is not large
956 * enough, but we ignore that case to avoid having to do extra dynamic
957 * allocation for the path and hope that 4096 is enough - this is
958 * ok in the Linux/Solaris case below at least...
961 gchar buf[4096];
962 guint buf_size = sizeof (buf);
964 name = NULL;
965 if (_NSGetExecutablePath (buf, &buf_size) == 0)
966 name = g_strdup (buf);
968 if (name == NULL) {
969 fallback ();
970 return;
973 #endif
975 resolvedname = mono_path_resolve_symlinks (name);
977 bindir = g_path_get_dirname (resolvedname);
978 installdir = g_path_get_dirname (bindir);
979 root = g_build_path (G_DIR_SEPARATOR_S, installdir, "lib", NULL);
981 config = g_build_filename (root, "..", "etc", NULL);
982 #ifdef HOST_WIN32
983 mono_set_dirs (root, config);
984 #else
985 if (g_file_test (root, G_FILE_TEST_EXISTS) && g_file_test (config, G_FILE_TEST_EXISTS))
986 mono_set_dirs (root, config);
987 else
988 fallback ();
989 #endif
991 g_free (config);
992 g_free (root);
993 g_free (installdir);
994 g_free (bindir);
995 g_free (name);
996 g_free (resolvedname);
997 #elif defined(DISABLE_MONO_AUTODETECTION)
998 fallback ();
999 #else
1000 char buf [4096];
1001 int s;
1002 char *str;
1004 #if defined(HAVE_READLINK)
1005 /* Linux style */
1006 s = readlink ("/proc/self/exe", buf, sizeof (buf)-1);
1007 #else
1008 s = -1;
1009 #endif
1011 if (s != -1){
1012 buf [s] = 0;
1013 set_dirs (buf);
1014 return;
1017 /* Solaris 10 style */
1018 str = g_strdup_printf ("/proc/%d/path/a.out", getpid ());
1020 #if defined(HAVE_READLINK)
1021 s = readlink (str, buf, sizeof (buf)-1);
1022 #else
1023 s = -1;
1024 #endif
1026 g_free (str);
1027 if (s != -1){
1028 buf [s] = 0;
1029 set_dirs (buf);
1030 return;
1032 fallback ();
1033 #endif
1037 * mono_assemblies_init:
1039 * Initialize global variables used by this module.
1041 void
1042 mono_assemblies_init (void)
1045 * Initialize our internal paths if we have not been initialized yet.
1046 * This happens when embedders use Mono.
1048 if (mono_assembly_getrootdir () == NULL)
1049 mono_set_rootdir ();
1051 check_path_env ();
1052 #ifndef DISABLE_GAC
1053 check_extra_gac_path_env ();
1054 #endif
1056 mono_os_mutex_init_recursive (&assemblies_mutex);
1057 mono_os_mutex_init (&assembly_binding_mutex);
1059 #ifndef DISABLE_DESKTOP_LOADER
1060 assembly_remapping_table = g_hash_table_new (g_str_hash, g_str_equal);
1062 int i;
1063 for (i = 0; i < G_N_ELEMENTS (framework_assemblies); ++i)
1064 g_hash_table_insert (assembly_remapping_table, (void*)framework_assemblies [i].assembly_name, (void*)&framework_assemblies [i]);
1066 #endif
1067 mono_install_assembly_asmctx_from_path_hook (assembly_loadfrom_asmctx_from_path, NULL);
1071 static void
1072 mono_assembly_binding_lock (void)
1074 mono_locks_os_acquire (&assembly_binding_mutex, AssemblyBindingLock);
1077 static void
1078 mono_assembly_binding_unlock (void)
1080 mono_locks_os_release (&assembly_binding_mutex, AssemblyBindingLock);
1083 gboolean
1084 mono_assembly_fill_assembly_name_full (MonoImage *image, MonoAssemblyName *aname, gboolean copyBlobs)
1086 MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
1087 guint32 cols [MONO_ASSEMBLY_SIZE];
1088 gint32 machine, flags;
1090 if (!t->rows)
1091 return FALSE;
1093 mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
1095 aname->hash_len = 0;
1096 aname->hash_value = NULL;
1097 aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
1098 if (copyBlobs)
1099 aname->name = g_strdup (aname->name);
1100 aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
1101 if (copyBlobs)
1102 aname->culture = g_strdup (aname->culture);
1103 aname->flags = cols [MONO_ASSEMBLY_FLAGS];
1104 aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
1105 aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
1106 aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
1107 aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER];
1108 aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG];
1109 if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
1110 guchar* token = (guchar *)g_malloc (8);
1111 gchar* encoded;
1112 const gchar* pkey;
1113 int len;
1115 pkey = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
1116 len = mono_metadata_decode_blob_size (pkey, &pkey);
1117 aname->public_key = (guchar*)pkey;
1119 mono_digest_get_public_token (token, aname->public_key, len);
1120 encoded = encode_public_tok (token, 8);
1121 g_strlcpy ((char*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1123 g_free (encoded);
1124 g_free (token);
1126 else {
1127 aname->public_key = NULL;
1128 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1131 if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
1132 aname->public_key = (guchar*)mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
1133 if (copyBlobs) {
1134 const gchar *pkey_end;
1135 int len = mono_metadata_decode_blob_size ((const gchar*) aname->public_key, &pkey_end);
1136 pkey_end += len; /* move to end */
1137 size_t size = pkey_end - (const gchar*)aname->public_key;
1138 guchar *tmp = g_new (guchar, size);
1139 memcpy (tmp, aname->public_key, size);
1140 aname->public_key = tmp;
1144 else
1145 aname->public_key = 0;
1147 machine = image->image_info->cli_header.coff.coff_machine;
1148 flags = image->image_info->cli_cli_header.ch_flags;
1149 switch (machine) {
1150 case COFF_MACHINE_I386:
1151 /* https://bugzilla.xamarin.com/show_bug.cgi?id=17632 */
1152 if (flags & (CLI_FLAGS_32BITREQUIRED|CLI_FLAGS_PREFERRED32BIT))
1153 aname->arch = MONO_PROCESSOR_ARCHITECTURE_X86;
1154 else if ((flags & 0x70) == 0x70)
1155 aname->arch = MONO_PROCESSOR_ARCHITECTURE_NONE;
1156 else
1157 aname->arch = MONO_PROCESSOR_ARCHITECTURE_MSIL;
1158 break;
1159 case COFF_MACHINE_IA64:
1160 aname->arch = MONO_PROCESSOR_ARCHITECTURE_IA64;
1161 break;
1162 case COFF_MACHINE_AMD64:
1163 aname->arch = MONO_PROCESSOR_ARCHITECTURE_AMD64;
1164 break;
1165 case COFF_MACHINE_ARM:
1166 aname->arch = MONO_PROCESSOR_ARCHITECTURE_ARM;
1167 break;
1168 default:
1169 break;
1172 return TRUE;
1176 * mono_assembly_fill_assembly_name:
1177 * \param image Image
1178 * \param aname Name
1179 * \returns TRUE if successful
1181 gboolean
1182 mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname)
1184 return mono_assembly_fill_assembly_name_full (image, aname, FALSE);
1188 * mono_stringify_assembly_name:
1189 * \param aname the assembly name.
1191 * Convert \p aname into its string format. The returned string is dynamically
1192 * allocated and should be freed by the caller.
1194 * \returns a newly allocated string with a string representation of
1195 * the assembly name.
1197 char*
1198 mono_stringify_assembly_name (MonoAssemblyName *aname)
1200 const char *quote = (aname->name && g_ascii_isspace (aname->name [0])) ? "\"" : "";
1202 return g_strdup_printf (
1203 "%s%s%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s%s",
1204 quote, aname->name, quote,
1205 aname->major, aname->minor, aname->build, aname->revision,
1206 aname->culture && *aname->culture? aname->culture: "neutral",
1207 aname->public_key_token [0] ? (char *)aname->public_key_token : "null",
1208 (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) ? ", Retargetable=Yes" : "");
1211 static gchar*
1212 assemblyref_public_tok (MonoImage *image, guint32 key_index, guint32 flags)
1214 const gchar *public_tok;
1215 int len;
1217 public_tok = mono_metadata_blob_heap (image, key_index);
1218 len = mono_metadata_decode_blob_size (public_tok, &public_tok);
1220 if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
1221 guchar token [8];
1222 mono_digest_get_public_token (token, (guchar*)public_tok, len);
1223 return encode_public_tok (token, 8);
1226 return encode_public_tok ((guchar*)public_tok, len);
1229 static gchar*
1230 assemblyref_public_tok_checked (MonoImage *image, guint32 key_index, guint32 flags, MonoError *error)
1232 const gchar *public_tok;
1233 int len;
1235 public_tok = mono_metadata_blob_heap_checked (image, key_index, error);
1236 return_val_if_nok (error, NULL);
1237 len = mono_metadata_decode_blob_size (public_tok, &public_tok);
1239 if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
1240 guchar token [8];
1241 mono_digest_get_public_token (token, (guchar*)public_tok, len);
1242 return encode_public_tok (token, 8);
1244 return encode_public_tok ((guchar*)public_tok, len);
1248 * mono_assembly_addref:
1249 * \param assembly the assembly to reference
1251 * This routine increments the reference count on a MonoAssembly.
1252 * The reference count is reduced every time the method mono_assembly_close() is
1253 * invoked.
1255 void
1256 mono_assembly_addref (MonoAssembly *assembly)
1258 mono_atomic_inc_i32 (&assembly->ref_count);
1262 * CAUTION: This table must be kept in sync with
1263 * ivkm/reflect/Fusion.cs
1266 #define SILVERLIGHT_KEY "7cec85d7bea7798e"
1267 #define WINFX_KEY "31bf3856ad364e35"
1268 #define ECMA_KEY "b77a5c561934e089"
1269 #define MSFINAL_KEY "b03f5f7f11d50a3a"
1270 #define COMPACTFRAMEWORK_KEY "969db8053d3322ac"
1272 typedef struct {
1273 const char *name;
1274 const char *from;
1275 const char *to;
1276 } KeyRemapEntry;
1278 static KeyRemapEntry key_remap_table[] = {
1279 { "CustomMarshalers", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1280 { "Microsoft.CSharp", WINFX_KEY, MSFINAL_KEY },
1281 { "Microsoft.VisualBasic", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1282 { "System", SILVERLIGHT_KEY, ECMA_KEY },
1283 { "System", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1284 { "System.ComponentModel.Composition", WINFX_KEY, ECMA_KEY },
1285 { "System.ComponentModel.DataAnnotations", "ddd0da4d3e678217", WINFX_KEY },
1286 { "System.Core", SILVERLIGHT_KEY, ECMA_KEY },
1287 { "System.Core", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1288 { "System.Data", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1289 { "System.Data.DataSetExtensions", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1290 { "System.Drawing", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1291 { "System.Messaging", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1292 // FIXME: MS uses MSFINAL_KEY for .NET 4.5
1293 { "System.Net", SILVERLIGHT_KEY, MSFINAL_KEY },
1294 { "System.Numerics", WINFX_KEY, ECMA_KEY },
1295 { "System.Runtime.Serialization", SILVERLIGHT_KEY, ECMA_KEY },
1296 { "System.Runtime.Serialization", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1297 { "System.ServiceModel", WINFX_KEY, ECMA_KEY },
1298 { "System.ServiceModel", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1299 { "System.ServiceModel.Web", SILVERLIGHT_KEY, WINFX_KEY },
1300 { "System.Web.Services", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1301 { "System.Windows", SILVERLIGHT_KEY, MSFINAL_KEY },
1302 { "System.Windows.Forms", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1303 { "System.Xml", SILVERLIGHT_KEY, ECMA_KEY },
1304 { "System.Xml", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1305 { "System.Xml.Linq", WINFX_KEY, ECMA_KEY },
1306 { "System.Xml.Linq", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1307 { "System.Xml.Serialization", WINFX_KEY, ECMA_KEY }
1310 static void
1311 remap_keys (MonoAssemblyName *aname)
1313 int i;
1314 for (i = 0; i < G_N_ELEMENTS (key_remap_table); i++) {
1315 const KeyRemapEntry *entry = &key_remap_table [i];
1317 if (strcmp (aname->name, entry->name) ||
1318 !mono_public_tokens_are_equal (aname->public_key_token, (const unsigned char*) entry->from))
1319 continue;
1321 memcpy (aname->public_key_token, entry->to, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1323 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1324 "Remapped public key token of retargetable assembly %s from %s to %s",
1325 aname->name, entry->from, entry->to);
1326 return;
1330 static MonoAssemblyName *
1331 mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_aname)
1333 const MonoRuntimeInfo *current_runtime;
1335 if (aname->name == NULL) return aname;
1337 current_runtime = mono_get_runtime_info ();
1339 if (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) {
1340 const AssemblyVersionSet* vset;
1342 /* Remap to current runtime */
1343 vset = &current_runtime->version_sets [0];
1345 memcpy (dest_aname, aname, sizeof(MonoAssemblyName));
1346 dest_aname->major = vset->major;
1347 dest_aname->minor = vset->minor;
1348 dest_aname->build = vset->build;
1349 dest_aname->revision = vset->revision;
1350 dest_aname->flags &= ~ASSEMBLYREF_RETARGETABLE_FLAG;
1352 /* Remap assembly name */
1353 if (!strcmp (aname->name, "System.Net"))
1354 dest_aname->name = g_strdup ("System");
1356 remap_keys (dest_aname);
1358 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1359 "The request to load the retargetable assembly %s v%d.%d.%d.%d was remapped to %s v%d.%d.%d.%d",
1360 aname->name,
1361 aname->major, aname->minor, aname->build, aname->revision,
1362 dest_aname->name,
1363 vset->major, vset->minor, vset->build, vset->revision
1366 return dest_aname;
1369 #ifndef DISABLE_DESKTOP_LOADER
1370 const AssemblyVersionMap *vmap = (AssemblyVersionMap *)g_hash_table_lookup (assembly_remapping_table, aname->name);
1371 if (vmap) {
1372 const AssemblyVersionSet* vset;
1373 int index = vmap->version_set_index;
1374 g_assert (index < G_N_ELEMENTS (current_runtime->version_sets));
1375 vset = &current_runtime->version_sets [index];
1377 if (vmap->framework_facade_assembly) {
1378 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Assembly %s is a framework Facade asseembly",
1379 aname->name);
1380 return aname;
1383 if (aname->major == vset->major && aname->minor == vset->minor &&
1384 aname->build == vset->build && aname->revision == vset->revision) {
1385 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Found assembly remapping for %s and was for the same version %d.%d.%d.%d",
1386 aname->name,
1387 aname->major, aname->minor, aname->build, aname->revision);
1388 return aname;
1391 if (vmap->only_lower_versions && compare_versions ((AssemblyVersionSet*)vset, aname) < 0) {
1392 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY,
1393 "Found lower-versions-only assembly remaping to load %s %d.%d.%d.%d but mapping has %d.%d.%d.%d",
1394 aname->name,
1395 aname->major, aname->minor, aname->build, aname->revision,
1396 vset->major, vset->minor, vset->build, vset->revision
1398 return aname;
1401 if ((aname->major | aname->minor | aname->build | aname->revision) != 0)
1402 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
1403 "The request to load the assembly %s v%d.%d.%d.%d was remapped to v%d.%d.%d.%d",
1404 aname->name,
1405 aname->major, aname->minor, aname->build, aname->revision,
1406 vset->major, vset->minor, vset->build, vset->revision
1409 memcpy (dest_aname, aname, sizeof(MonoAssemblyName));
1410 dest_aname->major = vset->major;
1411 dest_aname->minor = vset->minor;
1412 dest_aname->build = vset->build;
1413 dest_aname->revision = vset->revision;
1414 if (vmap->new_assembly_name != NULL) {
1415 dest_aname->name = vmap->new_assembly_name;
1416 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
1417 "The assembly name %s was remapped to %s",
1418 aname->name,
1419 dest_aname->name);
1421 return dest_aname;
1423 #endif
1425 return aname;
1429 * mono_assembly_get_assemblyref:
1430 * \param image pointer to the \c MonoImage to extract the information from.
1431 * \param index index to the assembly reference in the image.
1432 * \param aname pointer to a \c MonoAssemblyName that will hold the returned value.
1434 * Fills out the \p aname with the assembly name of the \p index assembly reference in \p image.
1436 void
1437 mono_assembly_get_assemblyref (MonoImage *image, int index, MonoAssemblyName *aname)
1439 MonoTableInfo *t;
1440 guint32 cols [MONO_ASSEMBLYREF_SIZE];
1441 const char *hash;
1443 t = &image->tables [MONO_TABLE_ASSEMBLYREF];
1445 mono_metadata_decode_row (t, index, cols, MONO_ASSEMBLYREF_SIZE);
1447 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
1448 aname->hash_len = mono_metadata_decode_blob_size (hash, &hash);
1449 aname->hash_value = hash;
1450 aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
1451 aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
1452 aname->flags = cols [MONO_ASSEMBLYREF_FLAGS];
1453 aname->major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
1454 aname->minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
1455 aname->build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
1456 aname->revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
1458 if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
1459 gchar *token = assemblyref_public_tok (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname->flags);
1460 g_strlcpy ((char*)aname->public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1461 g_free (token);
1462 } else {
1463 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1467 static MonoAssembly*
1468 load_reference_by_aname_refonly_asmctx (MonoAssemblyName *aname, MonoAssembly *assm, MonoImageOpenStatus *status)
1470 MonoAssembly *reference = NULL;
1471 g_assert (assm != NULL);
1472 g_assert (status != NULL);
1473 *status = MONO_IMAGE_OK;
1475 /* We use the loaded corlib */
1476 if (!strcmp (aname->name, MONO_ASSEMBLY_CORLIB_NAME)) {
1477 MonoAssemblyByNameRequest req;
1478 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
1479 req.requesting_assembly = assm;
1480 req.basedir = assm->basedir;
1481 reference = mono_assembly_request_byname (aname, &req, status);
1482 } else {
1483 reference = mono_assembly_loaded_internal (NULL, aname, TRUE);
1484 if (!reference)
1485 /* Try a postload search hook */
1486 reference = mono_assembly_invoke_search_hook_internal (NULL, assm, aname, TRUE, TRUE);
1490 * Here we must advice that the error was due to
1491 * a non loaded reference using the ReflectionOnly api
1493 if (!reference)
1494 reference = (MonoAssembly *)REFERENCE_MISSING;
1496 return reference;
1499 static MonoAssembly*
1500 load_reference_by_aname_default_asmctx (MonoAssemblyName *aname, MonoAssemblyLoadContext *alc, MonoAssembly *assm, MonoImageOpenStatus *status)
1502 MonoAssembly *reference = NULL;
1503 g_assert (status != NULL);
1504 *status = MONO_IMAGE_OK;
1506 /* we first try without setting the basedir: this can eventually result in a ResolveAssembly
1507 * event which is the MS .net compatible behaviour (the assemblyresolve_event3.cs test has been fixed
1508 * accordingly, it would fail on the MS runtime before).
1509 * The second load attempt has the basedir set to keep compatibility with the old mono behavior, for
1510 * example bug-349190.2.cs and who knows how much more code in the wild.
1512 MonoAssemblyByNameRequest req;
1513 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
1514 req.request.alc = alc;
1515 req.requesting_assembly = assm;
1516 reference = mono_assembly_request_byname (aname, &req, status);
1517 if (!reference && assm) {
1518 memset (&req, 0, sizeof (req));
1519 req.request.asmctx = MONO_ASMCTX_DEFAULT;
1520 req.request.alc = alc;
1521 req.requesting_assembly = assm;
1522 req.basedir = assm->basedir;
1523 reference = mono_assembly_request_byname (aname, &req, status);
1526 return reference;
1529 static MonoAssembly*
1530 load_reference_by_aname_loadfrom_asmctx (MonoAssemblyName *aname, MonoAssembly *requesting, MonoImageOpenStatus *status)
1532 MonoAssembly *reference = NULL;
1533 MonoAssemblyByNameRequest req;
1534 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_LOADFROM);
1535 req.requesting_assembly = requesting;
1536 req.basedir = requesting->basedir;
1537 /* Just like default search, but look in the requesting assembly basedir right away */
1538 reference = mono_assembly_request_byname (aname, &req, status);
1539 return reference;
1543 static MonoAssembly*
1544 load_reference_by_aname_individual_asmctx (MonoAssemblyName *aname, MonoAssembly *requesting, MonoImageOpenStatus *status)
1546 /* For an individual assembly, all references must already be loaded or
1547 * else we fire the assembly resolve event - similar to refonly - but
1548 * subject to remaping and binding.
1551 g_assert (status != NULL);
1553 MonoAssembly *reference = NULL;
1554 *status = MONO_IMAGE_OK;
1555 MonoAssemblyName maped_aname;
1556 MonoAssemblyName maped_name_pp;
1558 aname = mono_assembly_remap_version (aname, &maped_aname);
1559 aname = mono_assembly_apply_binding (aname, &maped_name_pp);
1561 reference = mono_assembly_loaded_internal (mono_domain_ambient_alc (mono_domain_get ()), aname, FALSE);
1562 /* Still try to load from application base directory, MONO_PATH or the
1563 * GAC. This is consistent with what .NET Framework (4.7) actually
1564 * does, rather than what the documentation implies: If `LoadFile` is
1565 * used to load an assembly into "no context"/individual assembly
1566 * context, the runtime will still load assemblies from the GAC or the
1567 * application base directory (e.g. `System.Runtime` will be loaded if
1568 * it wasn't already).
1569 * Moreover, those referenced assemblies are loaded in the default context.
1571 if (!reference) {
1572 MonoAssemblyByNameRequest req;
1573 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
1574 req.requesting_assembly = requesting;
1575 reference = mono_assembly_request_byname (aname, &req, status);
1577 if (!reference)
1578 reference = (MonoAssembly*)REFERENCE_MISSING;
1579 return reference;
1583 * mono_assembly_get_assemblyref:
1584 * \param image pointer to the \c MonoImage to extract the information from.
1585 * \param index index to the assembly reference in the image.
1586 * \param aname pointer to a \c MonoAssemblyName that will hold the returned value.
1587 * \param error set on error
1589 * Fills out the \p aname with the assembly name of the \p index assembly reference in \p image.
1591 * \returns TRUE on success, otherwise sets \p error and returns FALSE
1593 gboolean
1594 mono_assembly_get_assemblyref_checked (MonoImage *image, int index, MonoAssemblyName *aname, MonoError *error)
1596 MonoTableInfo *t;
1597 guint32 cols [MONO_ASSEMBLYREF_SIZE];
1598 const char *hash;
1600 t = &image->tables [MONO_TABLE_ASSEMBLYREF];
1602 if (!mono_metadata_decode_row_checked (image, t, index, cols, MONO_ASSEMBLYREF_SIZE, error))
1603 return FALSE;
1605 hash = mono_metadata_blob_heap_checked (image, cols [MONO_ASSEMBLYREF_HASH_VALUE], error);
1606 return_val_if_nok (error, FALSE);
1607 aname->hash_len = mono_metadata_decode_blob_size (hash, &hash);
1608 aname->hash_value = hash;
1609 aname->name = mono_metadata_string_heap_checked (image, cols [MONO_ASSEMBLYREF_NAME], error);
1610 return_val_if_nok (error, FALSE);
1611 aname->culture = mono_metadata_string_heap_checked (image, cols [MONO_ASSEMBLYREF_CULTURE], error);
1612 return_val_if_nok (error, FALSE);
1613 aname->flags = cols [MONO_ASSEMBLYREF_FLAGS];
1614 aname->major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
1615 aname->minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
1616 aname->build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
1617 aname->revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
1618 if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
1619 gchar *token = assemblyref_public_tok_checked (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname->flags, error);
1620 return_val_if_nok (error, FALSE);
1621 g_strlcpy ((char*)aname->public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1622 g_free (token);
1623 } else {
1624 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1626 return TRUE;
1630 * mono_assembly_load_reference:
1632 void
1633 mono_assembly_load_reference (MonoImage *image, int index)
1635 MonoAssembly *reference;
1636 MonoAssemblyName aname;
1637 MonoImageOpenStatus status;
1640 * image->references is shared between threads, so we need to access
1641 * it inside a critical section.
1643 mono_assemblies_lock ();
1644 if (!image->references) {
1645 MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLYREF];
1647 image->references = g_new0 (MonoAssembly *, t->rows + 1);
1648 image->nreferences = t->rows;
1650 reference = image->references [index];
1651 mono_assemblies_unlock ();
1652 if (reference)
1653 return;
1655 mono_assembly_get_assemblyref (image, index, &aname);
1657 if (image->assembly) {
1658 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
1659 char *aname_str = mono_stringify_assembly_name (&aname);
1660 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Loading reference %d of %s asmctx %s, looking for %s",
1661 index, image->name, mono_asmctx_get_name (&image->assembly->context),
1662 aname_str);
1663 g_free (aname_str);
1665 switch (mono_asmctx_get_kind (&image->assembly->context)) {
1666 case MONO_ASMCTX_DEFAULT:
1667 reference = load_reference_by_aname_default_asmctx (&aname, mono_image_get_alc (image), image->assembly, &status);
1668 break;
1669 case MONO_ASMCTX_REFONLY:
1670 reference = load_reference_by_aname_refonly_asmctx (&aname, image->assembly, &status);
1671 break;
1672 case MONO_ASMCTX_LOADFROM:
1673 reference = load_reference_by_aname_loadfrom_asmctx (&aname, image->assembly, &status);
1674 break;
1675 case MONO_ASMCTX_INDIVIDUAL:
1676 reference = load_reference_by_aname_individual_asmctx (&aname, image->assembly, &status);
1677 break;
1678 default:
1679 g_error ("Unexpected assembly load context kind %d for image %s.", mono_asmctx_get_kind (&image->assembly->context), image->name);
1680 break;
1682 } else {
1683 /* FIXME: can we establish that image->assembly is never NULL and this code is dead? */
1684 reference = load_reference_by_aname_default_asmctx (&aname, mono_image_get_alc (image), image->assembly, &status);
1687 if (reference == NULL){
1688 char *extra_msg;
1690 if (status == MONO_IMAGE_ERROR_ERRNO && errno == ENOENT) {
1691 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 : "" );
1692 } else if (status == MONO_IMAGE_ERROR_ERRNO) {
1693 extra_msg = g_strdup_printf ("System error: %s\n", strerror (errno));
1694 } else if (status == MONO_IMAGE_MISSING_ASSEMBLYREF) {
1695 extra_msg = g_strdup ("Cannot find an assembly referenced from this one.\n");
1696 } else if (status == MONO_IMAGE_IMAGE_INVALID) {
1697 extra_msg = g_strdup ("The file exists but is not a valid assembly.\n");
1698 } else {
1699 extra_msg = g_strdup ("");
1702 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, "The following assembly referenced from %s could not be loaded:\n"
1703 " Assembly: %s (assemblyref_index=%d)\n"
1704 " Version: %d.%d.%d.%d\n"
1705 " Public Key: %s\n%s",
1706 image->name, aname.name, index,
1707 aname.major, aname.minor, aname.build, aname.revision,
1708 strlen ((char*)aname.public_key_token) == 0 ? "(none)" : (char*)aname.public_key_token, extra_msg);
1709 g_free (extra_msg);
1713 mono_assemblies_lock ();
1714 if (reference == NULL) {
1715 /* Flag as not found */
1716 reference = (MonoAssembly *)REFERENCE_MISSING;
1719 if (!image->references [index]) {
1720 if (reference != REFERENCE_MISSING){
1721 mono_assembly_addref (reference);
1722 if (image->assembly)
1723 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Ref addref %s[%p] -> %s[%p]: %d",
1724 image->assembly->aname.name, image->assembly, reference->aname.name, reference, reference->ref_count);
1725 } else {
1726 if (image->assembly)
1727 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Failed to load assembly %s[%p].",
1728 image->assembly->aname.name, image->assembly);
1731 image->references [index] = reference;
1733 mono_assemblies_unlock ();
1735 if (image->references [index] != reference) {
1736 /* Somebody loaded it before us */
1737 mono_assembly_close (reference);
1742 * mono_assembly_load_references:
1743 * \param image
1744 * \param status
1745 * \deprecated There is no reason to use this method anymore, it does nothing
1747 * This method is now a no-op, it does nothing other than setting the \p status to \c MONO_IMAGE_OK
1749 void
1750 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
1752 /* This is a no-op now but it is part of the embedding API so we can't remove it */
1753 if (status)
1754 *status = MONO_IMAGE_OK;
1757 typedef struct AssemblyLoadHook AssemblyLoadHook;
1758 struct AssemblyLoadHook {
1759 AssemblyLoadHook *next;
1760 MonoAssemblyLoadFunc func;
1761 gpointer user_data;
1764 static AssemblyLoadHook *assembly_load_hook = NULL;
1767 * mono_assembly_invoke_load_hook:
1769 void
1770 mono_assembly_invoke_load_hook (MonoAssembly *ass)
1772 AssemblyLoadHook *hook;
1774 for (hook = assembly_load_hook; hook; hook = hook->next) {
1775 hook->func (ass, hook->user_data);
1780 * mono_install_assembly_load_hook:
1782 void
1783 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
1785 AssemblyLoadHook *hook;
1787 g_return_if_fail (func != NULL);
1789 hook = g_new0 (AssemblyLoadHook, 1);
1790 hook->func = func;
1791 hook->user_data = user_data;
1792 hook->next = assembly_load_hook;
1793 assembly_load_hook = hook;
1796 static void
1797 free_assembly_load_hooks (void)
1799 AssemblyLoadHook *hook, *next;
1801 for (hook = assembly_load_hook; hook; hook = next) {
1802 next = hook->next;
1803 g_free (hook);
1807 typedef struct AssemblySearchHook AssemblySearchHook;
1808 struct AssemblySearchHook {
1809 AssemblySearchHook *next;
1810 union {
1811 MonoAssemblySearchFunc v1;
1812 MonoAssemblySearchFuncV2 v2;
1813 } func;
1814 gboolean refonly;
1815 gboolean postload;
1816 int version;
1817 gpointer user_data;
1820 static AssemblySearchHook *assembly_search_hook = NULL;
1822 static MonoAssembly*
1823 mono_assembly_invoke_search_hook_internal (MonoAssemblyLoadContext *alc, MonoAssembly *requesting, MonoAssemblyName *aname, gboolean refonly, gboolean postload)
1825 AssemblySearchHook *hook;
1827 for (hook = assembly_search_hook; hook; hook = hook->next) {
1828 if ((hook->refonly == refonly) && (hook->postload == postload)) {
1829 MonoAssembly *ass;
1830 if (hook->version == 1) {
1831 ass = hook->func.v1 (aname, hook->user_data);
1832 } else {
1833 ERROR_DECL (hook_error);
1834 g_assert (hook->version == 2);
1835 ass = hook->func.v2 (alc, requesting, aname, refonly, postload, hook->user_data, hook_error);
1836 mono_error_assert_ok (hook_error); /* FIXME: proper error handling */
1838 if (ass)
1839 return ass;
1843 return NULL;
1847 * mono_assembly_invoke_search_hook:
1849 MonoAssembly*
1850 mono_assembly_invoke_search_hook (MonoAssemblyName *aname)
1852 return mono_assembly_invoke_search_hook_internal (NULL, NULL, aname, FALSE, FALSE);
1855 static void
1856 mono_install_assembly_search_hook_internal_v1 (MonoAssemblySearchFunc func, gpointer user_data, gboolean refonly, gboolean postload)
1858 AssemblySearchHook *hook;
1860 g_return_if_fail (func != NULL);
1862 hook = g_new0 (AssemblySearchHook, 1);
1863 hook->version = 1;
1864 hook->func.v1 = func;
1865 hook->user_data = user_data;
1866 hook->refonly = refonly;
1867 hook->postload = postload;
1868 hook->next = assembly_search_hook;
1869 assembly_search_hook = hook;
1872 void
1873 mono_install_assembly_search_hook_v2 (MonoAssemblySearchFuncV2 func, void *user_data, gboolean refonly, gboolean postload)
1875 if (func == NULL)
1876 return;
1878 AssemblySearchHook *hook = g_new0 (AssemblySearchHook, 1);
1879 hook->version = 2;
1880 hook->func.v2 = func;
1881 hook->user_data = user_data;
1882 hook->refonly = refonly;
1883 hook->postload = postload;
1884 hook->next = assembly_search_hook;
1885 assembly_search_hook = hook;
1889 * mono_install_assembly_search_hook:
1891 void
1892 mono_install_assembly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1894 mono_install_assembly_search_hook_internal_v1 (func, user_data, FALSE, FALSE);
1897 static void
1898 free_assembly_search_hooks (void)
1900 AssemblySearchHook *hook, *next;
1902 for (hook = assembly_search_hook; hook; hook = next) {
1903 next = hook->next;
1904 g_free (hook);
1909 * mono_install_assembly_refonly_search_hook:
1911 void
1912 mono_install_assembly_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1914 mono_install_assembly_search_hook_internal_v1 (func, user_data, TRUE, FALSE);
1918 * mono_install_assembly_postload_search_hook:
1920 void
1921 mono_install_assembly_postload_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1923 mono_install_assembly_search_hook_internal_v1 (func, user_data, FALSE, TRUE);
1926 void
1927 mono_install_assembly_postload_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1929 mono_install_assembly_search_hook_internal_v1 (func, user_data, TRUE, TRUE);
1933 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
1934 struct AssemblyPreLoadHook {
1935 AssemblyPreLoadHook *next;
1936 union {
1937 MonoAssemblyPreLoadFunc v1;
1938 MonoAssemblyPreLoadFuncV2 v2;
1939 } func;
1940 gpointer user_data;
1941 gint32 version;
1944 static AssemblyPreLoadHook *assembly_preload_hook = NULL;
1945 static AssemblyPreLoadHook *assembly_refonly_preload_hook = NULL;
1947 static MonoAssembly *
1948 invoke_assembly_preload_hook (MonoAssemblyLoadContext *alc, MonoAssemblyName *aname, gchar **apath)
1950 AssemblyPreLoadHook *hook;
1951 MonoAssembly *assembly;
1953 for (hook = assembly_preload_hook; hook; hook = hook->next) {
1954 if (hook->version == 1)
1955 assembly = hook->func.v1 (aname, apath, hook->user_data);
1956 else {
1957 ERROR_DECL (error);
1958 g_assert (hook->version == 2);
1959 assembly = hook->func.v2 (alc, aname, apath, FALSE, hook->user_data, error);
1960 /* TODO: propagage error out to callers */
1961 mono_error_assert_ok (error);
1963 if (assembly != NULL)
1964 return assembly;
1967 return NULL;
1970 static MonoAssembly *
1971 invoke_assembly_refonly_preload_hook (MonoAssemblyLoadContext *alc, MonoAssemblyName *aname, gchar **apath)
1973 AssemblyPreLoadHook *hook;
1974 MonoAssembly *assembly;
1976 for (hook = assembly_refonly_preload_hook; hook; hook = hook->next) {
1977 if (hook->version == 1)
1978 assembly = hook->func.v1 (aname, apath, hook->user_data);
1979 else {
1980 ERROR_DECL (error);
1981 g_assert (hook->version == 2);
1982 assembly = hook->func.v2 (alc, aname, apath, TRUE, hook->user_data, error);
1983 /* TODO: propagate error out to callers */
1984 mono_error_assert_ok (error);
1986 if (assembly != NULL)
1987 return assembly;
1990 return NULL;
1994 * mono_install_assembly_preload_hook:
1996 void
1997 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
1999 AssemblyPreLoadHook *hook;
2001 g_return_if_fail (func != NULL);
2003 hook = g_new0 (AssemblyPreLoadHook, 1);
2004 hook->version = 1;
2005 hook->func.v1 = func;
2006 hook->user_data = user_data;
2007 hook->next = assembly_preload_hook;
2008 assembly_preload_hook = hook;
2012 * mono_install_assembly_refonly_preload_hook:
2014 void
2015 mono_install_assembly_refonly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
2017 AssemblyPreLoadHook *hook;
2019 g_return_if_fail (func != NULL);
2021 hook = g_new0 (AssemblyPreLoadHook, 1);
2022 hook->version = 1;
2023 hook->func.v1 = func;
2024 hook->user_data = user_data;
2025 hook->next = assembly_refonly_preload_hook;
2026 assembly_refonly_preload_hook = hook;
2029 void
2030 mono_install_assembly_preload_hook_v2 (MonoAssemblyPreLoadFuncV2 func, gpointer user_data, gboolean refonly)
2032 AssemblyPreLoadHook *hook;
2034 g_return_if_fail (func != NULL);
2036 AssemblyPreLoadHook **hooks = refonly ? &assembly_refonly_preload_hook : &assembly_preload_hook;
2038 hook = g_new0 (AssemblyPreLoadHook, 1);
2039 hook->version = 2;
2040 hook->func.v2 = func;
2041 hook->user_data = user_data;
2042 hook->next = *hooks;
2044 *hooks = hook;
2047 static void
2048 free_assembly_preload_hooks (void)
2050 AssemblyPreLoadHook *hook, *next;
2052 for (hook = assembly_preload_hook; hook; hook = next) {
2053 next = hook->next;
2054 g_free (hook);
2057 for (hook = assembly_refonly_preload_hook; hook; hook = next) {
2058 next = hook->next;
2059 g_free (hook);
2063 typedef struct AssemblyAsmCtxFromPathHook AssemblyAsmCtxFromPathHook;
2064 struct AssemblyAsmCtxFromPathHook {
2065 AssemblyAsmCtxFromPathHook *next;
2066 MonoAssemblyAsmCtxFromPathFunc func;
2067 gpointer user_data;
2070 static AssemblyAsmCtxFromPathHook *assembly_asmctx_from_path_hook = NULL;
2073 * mono_install_assembly_asmctx_from_path_hook:
2075 * \param func Hook function
2076 * \param user_data User data
2078 * Installs a hook function \p func that when called with an absolute path name
2079 * returns \c TRUE and writes to \c out_asmctx if an assembly that name would
2080 * be found by that asmctx. The hooks are called in the order from most
2081 * recently added to oldest.
2084 void
2085 mono_install_assembly_asmctx_from_path_hook (MonoAssemblyAsmCtxFromPathFunc func, gpointer user_data)
2087 g_return_if_fail (func != NULL);
2089 AssemblyAsmCtxFromPathHook *hook = g_new0 (AssemblyAsmCtxFromPathHook, 1);
2090 hook->func = func;
2091 hook->user_data = user_data;
2092 hook->next = assembly_asmctx_from_path_hook;
2093 assembly_asmctx_from_path_hook = hook;
2097 * mono_assembly_invoke_asmctx_from_path_hook:
2099 * \param absfname absolute path name
2100 * \param requesting_assembly the \c MonoAssembly that requested the load, may be \c NULL
2101 * \param out_asmctx assembly context kind, written on output
2103 * Invokes hooks to find the assembly context that would have searched for the
2104 * given assembly name. Writes to \p out_asmctx the assembly context kind from
2105 * the first hook to return \c TRUE. \returns \c TRUE if any hook wrote to \p
2106 * out_asmctx, or \c FALSE otherwise.
2108 static gboolean
2109 assembly_invoke_asmctx_from_path_hook (const char *absfname, MonoAssembly *requesting_assembly, MonoAssemblyContextKind *out_asmctx)
2111 g_assert (absfname);
2112 g_assert (out_asmctx);
2113 AssemblyAsmCtxFromPathHook *hook;
2115 for (hook = assembly_asmctx_from_path_hook; hook; hook = hook->next) {
2116 *out_asmctx = MONO_ASMCTX_INDIVIDUAL;
2117 if (hook->func (absfname, requesting_assembly, hook->user_data, out_asmctx))
2118 return TRUE;
2120 return FALSE;
2124 static void
2125 free_assembly_asmctx_from_path_hooks (void)
2127 AssemblyAsmCtxFromPathHook *hook, *next;
2129 for (hook = assembly_asmctx_from_path_hook; hook; hook = next) {
2130 next = hook->next;
2131 g_free (hook);
2135 static gchar *
2136 absolute_dir (const gchar *filename)
2138 gchar *cwd;
2139 gchar *mixed;
2140 gchar **parts;
2141 gchar *part;
2142 GList *list, *tmp;
2143 GString *result;
2144 gchar *res;
2145 gint i;
2147 if (g_path_is_absolute (filename)) {
2148 part = g_path_get_dirname (filename);
2149 res = g_strconcat (part, G_DIR_SEPARATOR_S, NULL);
2150 g_free (part);
2151 return res;
2154 cwd = g_get_current_dir ();
2155 mixed = g_build_filename (cwd, filename, NULL);
2156 parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
2157 g_free (mixed);
2158 g_free (cwd);
2160 list = NULL;
2161 for (i = 0; (part = parts [i]) != NULL; i++) {
2162 if (!strcmp (part, "."))
2163 continue;
2165 if (!strcmp (part, "..")) {
2166 if (list && list->next) /* Don't remove root */
2167 list = g_list_delete_link (list, list);
2168 } else {
2169 list = g_list_prepend (list, part);
2173 result = g_string_new ("");
2174 list = g_list_reverse (list);
2176 /* Ignores last data pointer, which should be the filename */
2177 for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next){
2178 if (tmp->data)
2179 g_string_append_printf (result, "%s%c", (char *) tmp->data,
2180 G_DIR_SEPARATOR);
2183 res = result->str;
2184 g_string_free (result, FALSE);
2185 g_list_free (list);
2186 g_strfreev (parts);
2187 if (*res == '\0') {
2188 g_free (res);
2189 return g_strdup (".");
2192 return res;
2195 /**
2196 * mono_assembly_open_from_bundle:
2197 * \param filename Filename requested
2198 * \param status return status code
2200 * This routine tries to open the assembly specified by \p filename from the
2201 * defined bundles, if found, returns the MonoImage for it, if not found
2202 * returns NULL
2204 MonoImage *
2205 mono_assembly_open_from_bundle (MonoAssemblyLoadContext *alc, const char *filename, MonoImageOpenStatus *status, gboolean refonly)
2207 int i;
2208 char *name;
2209 gchar *lowercase_filename;
2210 MonoImage *image = NULL;
2211 gboolean is_satellite = FALSE;
2213 * we do a very simple search for bundled assemblies: it's not a general
2214 * purpose assembly loading mechanism.
2217 if (!bundles)
2218 return NULL;
2220 lowercase_filename = g_utf8_strdown (filename, -1);
2221 is_satellite = g_str_has_suffix (lowercase_filename, ".resources.dll");
2222 g_free (lowercase_filename);
2223 name = g_path_get_basename (filename);
2224 for (i = 0; !image && bundles [i]; ++i) {
2225 if (strcmp (bundles [i]->name, is_satellite ? filename : name) == 0) {
2226 image = mono_image_open_from_data_internal (alc, (char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly, FALSE, name);
2227 break;
2230 if (image) {
2231 mono_image_addref (image);
2232 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Loader loaded assembly from bundle: '%s'.", is_satellite ? filename : name);
2233 g_free (name);
2234 return image;
2236 g_free (name);
2237 return NULL;
2241 * mono_assembly_open_full:
2242 * \param filename the file to load
2243 * \param status return status code
2244 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
2246 * This loads an assembly from the specified \p filename. The \p filename allows
2247 * a local URL (starting with a \c file:// prefix). If a file prefix is used, the
2248 * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file
2249 * is treated as a local path.
2251 * First, an attempt is made to load the assembly from the bundled executable (for those
2252 * deployments that have been done with the \c mkbundle tool or for scenarios where the
2253 * assembly has been registered as an embedded assembly). If this is not the case, then
2254 * the assembly is loaded from disk using `api:mono_image_open_full`.
2256 * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of
2257 * the assembly is made.
2259 * If \p refonly is set to true, then the assembly is loaded purely for inspection with
2260 * the \c System.Reflection API.
2262 * \returns NULL on error, with the \p status set to an error code, or a pointer
2263 * to the assembly.
2265 MonoAssembly *
2266 mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
2268 MonoAssembly *res;
2269 MONO_ENTER_GC_UNSAFE;
2270 MonoAssemblyOpenRequest req;
2271 mono_assembly_request_prepare (&req.request, sizeof (req), refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT);
2272 res = mono_assembly_request_open (filename, &req, status);
2273 MONO_EXIT_GC_UNSAFE;
2274 return res;
2277 static gboolean
2278 assembly_loadfrom_asmctx_from_path (const char *filename, MonoAssembly *requesting_assembly,
2279 gpointer user_data, MonoAssemblyContextKind *out_asmctx) {
2280 if (requesting_assembly && mono_asmctx_get_kind (&requesting_assembly->context) == MONO_ASMCTX_LOADFROM) {
2281 if (mono_path_filename_in_basedir (filename, requesting_assembly->basedir)) {
2282 *out_asmctx = MONO_ASMCTX_LOADFROM;
2283 return TRUE;
2286 return FALSE;
2289 MonoAssembly *
2290 mono_assembly_request_open (const char *filename, const MonoAssemblyOpenRequest *open_req,
2291 MonoImageOpenStatus *status)
2293 MonoImage *image;
2294 MonoAssembly *ass;
2295 MonoImageOpenStatus def_status;
2296 gchar *fname;
2297 gchar *new_fname;
2298 gboolean loaded_from_bundle;
2300 MonoAssemblyLoadRequest load_req;
2301 /* we will be overwriting the load request's asmctx.*/
2302 memcpy (&load_req, &open_req->request, sizeof (load_req));
2304 g_return_val_if_fail (filename != NULL, NULL);
2306 if (!status)
2307 status = &def_status;
2308 *status = MONO_IMAGE_OK;
2310 if (strncmp (filename, "file://", 7) == 0) {
2311 GError *gerror = NULL;
2312 gchar *uri = (gchar *) filename;
2313 gchar *tmpuri;
2316 * MS allows file://c:/... and fails on file://localhost/c:/...
2317 * They also throw an IndexOutOfRangeException if "file://"
2319 if (uri [7] != '/')
2320 uri = g_strdup_printf ("file:///%s", uri + 7);
2322 tmpuri = uri;
2323 uri = mono_escape_uri_string (tmpuri);
2324 fname = g_filename_from_uri (uri, NULL, &gerror);
2325 g_free (uri);
2327 if (tmpuri != filename)
2328 g_free (tmpuri);
2330 if (gerror != NULL) {
2331 g_warning ("%s\n", gerror->message);
2332 g_error_free (gerror);
2333 fname = g_strdup (filename);
2335 } else {
2336 fname = g_strdup (filename);
2339 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2340 "Assembly Loader probing location: '%s'.", fname);
2342 new_fname = NULL;
2343 if (!mono_assembly_is_in_gac (fname)) {
2344 ERROR_DECL (error);
2345 new_fname = mono_make_shadow_copy (fname, error);
2346 if (!is_ok (error)) {
2347 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2348 "Assembly Loader shadow copy error: %s.", mono_error_get_message (error));
2349 mono_error_cleanup (error);
2350 *status = MONO_IMAGE_IMAGE_INVALID;
2351 g_free (fname);
2352 return NULL;
2355 if (load_req.asmctx != MONO_ASMCTX_REFONLY) {
2356 MonoAssemblyContextKind out_asmctx;
2357 /* If the path belongs to the appdomain base dir or the
2358 * base dir of the requesting assembly, load the
2359 * assembly in the corresponding asmctx.
2361 if (assembly_invoke_asmctx_from_path_hook (fname, open_req->requesting_assembly, &out_asmctx))
2362 load_req.asmctx = out_asmctx;
2364 } else {
2365 if (load_req.asmctx != MONO_ASMCTX_REFONLY) {
2366 /* GAC assemblies always in default context or refonly context. */
2367 load_req.asmctx = MONO_ASMCTX_DEFAULT;
2370 if (new_fname && new_fname != fname) {
2371 g_free (fname);
2372 fname = new_fname;
2373 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2374 "Assembly Loader shadow-copied assembly to: '%s'.", fname);
2377 image = NULL;
2379 const gboolean refonly = load_req.asmctx == MONO_ASMCTX_REFONLY;
2380 /* for LoadFrom(string), LoadFile(string) and Load(byte[]), allow them
2381 * to load problematic images. Not sure if ReflectionOnlyLoad(string)
2382 * and ReflectionOnlyLoadFrom(string) should also be allowed - let's
2383 * say, yes.
2385 const gboolean load_from_context = load_req.asmctx == MONO_ASMCTX_LOADFROM || load_req.asmctx == MONO_ASMCTX_INDIVIDUAL || load_req.asmctx == MONO_ASMCTX_REFONLY;
2387 // If VM built with mkbundle
2388 loaded_from_bundle = FALSE;
2389 if (bundles != NULL) {
2390 image = mono_assembly_open_from_bundle (load_req.alc, fname, status, refonly);
2391 loaded_from_bundle = image != NULL;
2394 if (!image)
2395 image = mono_image_open_a_lot (load_req.alc, fname, status, refonly, load_from_context);
2397 if (!image){
2398 if (*status == MONO_IMAGE_OK)
2399 *status = MONO_IMAGE_ERROR_ERRNO;
2400 g_free (fname);
2401 return NULL;
2404 if (load_req.asmctx == MONO_ASMCTX_LOADFROM || load_req.asmctx == MONO_ASMCTX_INDIVIDUAL) {
2405 MonoAssembly *redirected_asm = NULL;
2406 MonoImageOpenStatus new_status = MONO_IMAGE_OK;
2407 if ((redirected_asm = chain_redirections_loadfrom (image, &new_status))) {
2408 mono_image_close (image);
2409 image = redirected_asm->image;
2410 mono_image_addref (image); /* so that mono_image_close, below, has something to do */
2411 /* fall thru to if (image->assembly) below */
2412 } else if (new_status != MONO_IMAGE_OK) {
2413 *status = new_status;
2414 mono_image_close (image);
2415 g_free (fname);
2416 return NULL;
2420 if (image->assembly) {
2421 /* We want to return the MonoAssembly that's already loaded,
2422 * but if we're using the strict assembly loader, we also need
2423 * to check that the previously loaded assembly matches the
2424 * predicate. It could be that we previously loaded a
2425 * different version that happens to have the filename that
2426 * we're currently probing. */
2427 if (mono_loader_get_strict_strong_names () &&
2428 load_req.predicate && !load_req.predicate (image->assembly, load_req.predicate_ud)) {
2429 mono_image_close (image);
2430 g_free (fname);
2431 return NULL;
2432 } else {
2433 /* Already loaded by another appdomain */
2434 mono_assembly_invoke_load_hook (image->assembly);
2435 mono_image_close (image);
2436 g_free (fname);
2437 return image->assembly;
2441 ass = mono_assembly_request_load_from (image, fname, &load_req, status);
2443 if (ass) {
2444 if (!loaded_from_bundle)
2445 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2446 "Assembly Loader loaded assembly from location: '%s'.", filename);
2447 if (!refonly)
2448 mono_config_for_assembly_internal (ass->image);
2451 /* Clear the reference added by mono_image_open */
2452 mono_image_close (image);
2454 g_free (fname);
2456 return ass;
2459 static void
2460 free_item (gpointer val, gpointer user_data)
2462 g_free (val);
2466 * mono_assembly_load_friends:
2467 * \param ass an assembly
2469 * Load the list of friend assemblies that are allowed to access
2470 * the assembly's internal types and members. They are stored as assembly
2471 * names in custom attributes.
2473 * This is an internal method, we need this because when we load mscorlib
2474 * we do not have the internals visible cattr loaded yet,
2475 * so we need to load these after we initialize the runtime.
2477 * LOCKING: Acquires the assemblies lock plus the loader lock.
2479 void
2480 mono_assembly_load_friends (MonoAssembly* ass)
2482 ERROR_DECL (error);
2483 int i;
2484 MonoCustomAttrInfo* attrs;
2485 GSList *list;
2487 if (ass->friend_assembly_names_inited)
2488 return;
2490 attrs = mono_custom_attrs_from_assembly_checked (ass, FALSE, error);
2491 mono_error_assert_ok (error);
2492 if (!attrs) {
2493 mono_assemblies_lock ();
2494 ass->friend_assembly_names_inited = TRUE;
2495 mono_assemblies_unlock ();
2496 return;
2499 mono_assemblies_lock ();
2500 if (ass->friend_assembly_names_inited) {
2501 mono_assemblies_unlock ();
2502 return;
2504 mono_assemblies_unlock ();
2506 list = NULL;
2508 * We build the list outside the assemblies lock, the worse that can happen
2509 * is that we'll need to free the allocated list.
2511 for (i = 0; i < attrs->num_attrs; ++i) {
2512 MonoCustomAttrEntry *attr = &attrs->attrs [i];
2513 MonoAssemblyName *aname;
2514 const gchar *data;
2515 uint32_t data_length;
2516 gchar *data_with_terminator;
2517 /* Do some sanity checking */
2518 if (!attr->ctor || attr->ctor->klass != mono_class_try_get_internals_visible_class ())
2519 continue;
2520 if (attr->data_size < 4)
2521 continue;
2522 data = (const char*)attr->data;
2523 /* 0xFF means null string, see custom attr format */
2524 if (data [0] != 1 || data [1] != 0 || (data [2] & 0xFF) == 0xFF)
2525 continue;
2526 data_length = mono_metadata_decode_value (data + 2, &data);
2527 data_with_terminator = (char *)g_memdup (data, data_length + 1);
2528 data_with_terminator[data_length] = 0;
2529 aname = g_new0 (MonoAssemblyName, 1);
2530 /*g_print ("friend ass: %s\n", data);*/
2531 if (mono_assembly_name_parse_full (data_with_terminator, aname, TRUE, NULL, NULL)) {
2532 list = g_slist_prepend (list, aname);
2533 } else {
2534 g_free (aname);
2536 g_free (data_with_terminator);
2538 mono_custom_attrs_free (attrs);
2540 mono_assemblies_lock ();
2541 if (ass->friend_assembly_names_inited) {
2542 mono_assemblies_unlock ();
2543 g_slist_foreach (list, free_item, NULL);
2544 g_slist_free (list);
2545 return;
2547 ass->friend_assembly_names = list;
2549 /* Because of the double checked locking pattern above */
2550 mono_memory_barrier ();
2551 ass->friend_assembly_names_inited = TRUE;
2552 mono_assemblies_unlock ();
2555 struct HasReferenceAssemblyAttributeIterData {
2556 gboolean has_attr;
2559 static gboolean
2560 has_reference_assembly_attribute_iterator (MonoImage *image, guint32 typeref_scope_token, const char *nspace, const char *name, guint32 method_token, gpointer user_data)
2562 gboolean stop_scanning = FALSE;
2563 struct HasReferenceAssemblyAttributeIterData *iter_data = (struct HasReferenceAssemblyAttributeIterData*)user_data;
2565 if (!strcmp (name, "ReferenceAssemblyAttribute") && !strcmp (nspace, "System.Runtime.CompilerServices")) {
2566 /* Note we don't check the assembly name, same as coreCLR. */
2567 iter_data->has_attr = TRUE;
2568 stop_scanning = TRUE;
2571 return stop_scanning;
2575 * mono_assembly_has_reference_assembly_attribute:
2576 * \param assembly a MonoAssembly
2577 * \param error set on error.
2579 * \returns TRUE if \p assembly has the \c System.Runtime.CompilerServices.ReferenceAssemblyAttribute set.
2580 * On error returns FALSE and sets \p error.
2582 gboolean
2583 mono_assembly_has_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error)
2585 g_assert (assembly && assembly->image);
2586 /* .NET Framework appears to ignore the attribute on dynamic
2587 * assemblies, so don't call this function for dynamic assemblies. */
2588 g_assert (!image_is_dynamic (assembly->image));
2589 error_init (error);
2592 * This might be called during assembly loading, so do everything using the low-level
2593 * metadata APIs.
2596 struct HasReferenceAssemblyAttributeIterData iter_data = { FALSE };
2598 mono_assembly_metadata_foreach_custom_attr (assembly, &has_reference_assembly_attribute_iterator, &iter_data);
2600 return iter_data.has_attr;
2604 * chain_redirections_loadfrom:
2605 * \param image a MonoImage that we wanted to load using LoadFrom context
2606 * \param status set if there was an error opening the redirected image
2608 * Check if we need to open a different image instead of the given one for some reason.
2609 * Returns NULL and sets status to \c MONO_IMAGE_OK if the given image was good.
2611 * Otherwise returns the assembly that we opened instead or sets status if
2612 * there was a problem opening the redirected image.
2615 MonoAssembly*
2616 chain_redirections_loadfrom (MonoImage *image, MonoImageOpenStatus *out_status)
2618 MonoImageOpenStatus status = MONO_IMAGE_OK;
2619 MonoAssembly *redirected = NULL;
2621 redirected = mono_assembly_binding_applies_to_image (image, &status);
2622 if (redirected || status != MONO_IMAGE_OK) {
2623 *out_status = status;
2624 return redirected;
2627 redirected = mono_problematic_image_reprobe (image, &status);
2628 if (redirected || status != MONO_IMAGE_OK) {
2629 *out_status = status;
2630 return redirected;
2633 *out_status = MONO_IMAGE_OK;
2634 return NULL;
2638 * mono_assembly_binding_applies_to_image:
2639 * \param image The image whose assembly name we should check
2640 * \param status sets on error;
2642 * Get the \c MonoAssemblyName from the given \p image metadata and apply binding redirects to it.
2643 * If the resulting name is different from the name in the image, load that \c MonoAssembly instead
2645 * \returns the loaded \c MonoAssembly, or NULL if no binding redirection applied.
2648 MonoAssembly*
2649 mono_assembly_binding_applies_to_image (MonoImage* image, MonoImageOpenStatus *status)
2651 g_assert (status != NULL);
2653 /* This is a "fun" one now.
2654 * For LoadFrom ("/basedir/some.dll") or LoadFile("/basedir/some.dll") or Load(byte[])),
2655 * apparently what we're meant to do is:
2656 * 1. probe the assembly name from some.dll (or the byte array)
2657 * 2. apply binding redirects
2658 * 3. If we get some other different name, drop this image and use
2659 * the binding redirected name to probe.
2660 * 4. Return the new assembly.
2662 MonoAssemblyName probed_aname, dest_name;
2663 if (!mono_assembly_fill_assembly_name_full (image, &probed_aname, TRUE)) {
2664 if (*status == MONO_IMAGE_OK)
2665 *status = MONO_IMAGE_IMAGE_INVALID;
2666 return NULL;
2668 MonoAssembly *result_ass = NULL;
2669 MonoAssemblyName *result_name = &probed_aname;
2670 result_name = mono_assembly_apply_binding (result_name, &dest_name);
2671 if (result_name != &probed_aname && !mono_assembly_names_equal (result_name, &probed_aname)) {
2672 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
2673 char *probed_fullname = mono_stringify_assembly_name (&probed_aname);
2674 char *result_fullname = mono_stringify_assembly_name (result_name);
2675 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);
2676 g_free (probed_fullname);
2677 g_free (result_fullname);
2679 const char *new_basedir = NULL; /* FIXME: null? - do a test of this */
2680 MonoAssemblyContextKind new_asmctx = MONO_ASMCTX_DEFAULT; /* FIXME: default? or? */
2681 MonoAssembly *new_requesting = NULL; /* this seems okay */
2682 MonoImageOpenStatus new_status = MONO_IMAGE_OK;
2684 MonoAssemblyByNameRequest new_req;
2685 mono_assembly_request_prepare (&new_req.request, sizeof (new_req), new_asmctx);
2686 new_req.requesting_assembly = new_requesting;
2687 new_req.basedir = new_basedir;
2688 result_ass = mono_assembly_request_byname (result_name, &new_req, &new_status);
2690 if (result_ass && new_status == MONO_IMAGE_OK) {
2691 g_assert (result_ass->image->assembly != NULL);
2692 } else {
2693 *status = new_status;
2696 mono_assembly_name_free (&probed_aname);
2697 return result_ass;
2701 * mono_problematic_image_reprobe:
2702 * \param image A MonoImage
2703 * \param status set on error
2705 * If the given image is problematic for mono (see image.c), then try to load
2706 * by assembly name in the default context (which should succeed with Mono's
2707 * own implementations of those assemblies).
2709 * Returns NULL and sets status to MONO_IMAGE_OK if no redirect is needed.
2711 * Otherwise returns the assembly we were redirected to, or NULL and sets a
2712 * non-ok status on failure.
2714 * IMPORTANT NOTE: Don't call this if \c image was found by probing the search
2715 * path, you will end up in a loop and a stack overflow.
2717 MonoAssembly*
2718 mono_problematic_image_reprobe (MonoImage *image, MonoImageOpenStatus *status)
2720 g_assert (status != NULL);
2722 if (G_LIKELY (!mono_is_problematic_image (image))) {
2723 *status = MONO_IMAGE_OK;
2724 return NULL;
2726 MonoAssemblyName probed_aname;
2727 if (!mono_assembly_fill_assembly_name_full (image, &probed_aname, TRUE)) {
2728 *status = MONO_IMAGE_IMAGE_INVALID;
2729 return NULL;
2731 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
2732 char *probed_fullname = mono_stringify_assembly_name (&probed_aname);
2733 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);
2734 g_free (probed_fullname);
2736 const char *new_basedir = NULL;
2737 MonoAssemblyContextKind new_asmctx = MONO_ASMCTX_DEFAULT;
2738 MonoAssembly *new_requesting = NULL;
2739 MonoImageOpenStatus new_status = MONO_IMAGE_OK;
2740 MonoAssemblyByNameRequest new_req;
2741 mono_assembly_request_prepare (&new_req.request, sizeof (new_req), new_asmctx);
2742 new_req.requesting_assembly = new_requesting;
2743 new_req.basedir = new_basedir;
2744 // Note: this interacts with mono_image_open_a_lot (). If the path from
2745 // which we tried to load the problematic image is among the probing
2746 // paths, the MonoImage will be in the hash of loaded images and we
2747 // would just get it back again here, except for the code there that
2748 // mitigates the situation. Instead
2749 MonoAssembly *result_ass = mono_assembly_request_byname (&probed_aname, &new_req, &new_status);
2751 if (! (result_ass && new_status == MONO_IMAGE_OK)) {
2752 *status = new_status;
2754 mono_assembly_name_free (&probed_aname);
2755 return result_ass;
2758 * mono_assembly_open:
2759 * \param filename Opens the assembly pointed out by this name
2760 * \param status return status code
2762 * This loads an assembly from the specified \p filename. The \p filename allows
2763 * a local URL (starting with a \c file:// prefix). If a file prefix is used, the
2764 * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file
2765 * is treated as a local path.
2767 * First, an attempt is made to load the assembly from the bundled executable (for those
2768 * deployments that have been done with the \c mkbundle tool or for scenarios where the
2769 * assembly has been registered as an embedded assembly). If this is not the case, then
2770 * the assembly is loaded from disk using `api:mono_image_open_full`.
2772 * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of
2773 * the assembly is made.
2775 * \returns a pointer to the \c MonoAssembly if \p filename contains a valid
2776 * assembly or NULL on error. Details about the error are stored in the
2777 * \p status variable.
2779 MonoAssembly *
2780 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
2782 MonoAssembly *res;
2783 MONO_ENTER_GC_UNSAFE;
2784 MonoAssemblyOpenRequest req;
2785 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
2786 res = mono_assembly_request_open (filename, &req, status);
2787 MONO_EXIT_GC_UNSAFE;
2788 return res;
2792 * mono_assembly_load_from_full:
2793 * \param image Image to load the assembly from
2794 * \param fname assembly name to associate with the assembly
2795 * \param status returns the status condition
2796 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
2798 * If the provided \p image has an assembly reference, it will process the given
2799 * image as an assembly with the given name.
2801 * Most likely you want to use the `api:mono_assembly_load_full` method instead.
2803 * Returns: A valid pointer to a \c MonoAssembly* on success and the \p status will be
2804 * set to \c MONO_IMAGE_OK; or NULL on error.
2806 * If there is an error loading the assembly the \p status will indicate the
2807 * reason with \p status being set to \c MONO_IMAGE_INVALID if the
2808 * image did not contain an assembly reference table.
2810 MonoAssembly *
2811 mono_assembly_load_from_full (MonoImage *image, const char*fname,
2812 MonoImageOpenStatus *status, gboolean refonly)
2814 MonoAssembly *res;
2815 MONO_ENTER_GC_UNSAFE;
2816 MonoAssemblyLoadRequest req;
2817 MonoImageOpenStatus def_status;
2818 if (!status)
2819 status = &def_status;
2820 mono_assembly_request_prepare (&req, sizeof (req), refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT);
2821 res = mono_assembly_request_load_from (image, fname, &req, status);
2822 MONO_EXIT_GC_UNSAFE;
2823 return res;
2826 MonoAssembly *
2827 mono_assembly_request_load_from (MonoImage *image, const char *fname,
2828 const MonoAssemblyLoadRequest *req,
2829 MonoImageOpenStatus *status)
2831 MonoAssemblyContextKind asmctx;
2832 MonoAssemblyCandidatePredicate predicate;
2833 gpointer user_data;
2835 MonoAssembly *ass, *ass2;
2836 char *base_dir;
2838 g_assert (status != NULL);
2840 asmctx = req->asmctx;
2841 predicate = req->predicate;
2842 user_data = req->predicate_ud;
2844 if (!image->tables [MONO_TABLE_ASSEMBLY].rows) {
2845 /* 'image' doesn't have a manifest -- maybe someone is trying to Assembly.Load a .netmodule */
2846 *status = MONO_IMAGE_IMAGE_INVALID;
2847 return NULL;
2850 #if defined (HOST_WIN32)
2852 gchar *tmp_fn;
2853 int i;
2855 tmp_fn = g_strdup (fname);
2856 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
2857 if (tmp_fn [i] == '/')
2858 tmp_fn [i] = '\\';
2861 base_dir = absolute_dir (tmp_fn);
2862 g_free (tmp_fn);
2864 #else
2865 base_dir = absolute_dir (fname);
2866 #endif
2869 * Create assembly struct, and enter it into the assembly cache
2871 ass = g_new0 (MonoAssembly, 1);
2872 ass->basedir = base_dir;
2873 ass->context.kind = asmctx;
2874 ass->image = image;
2876 MONO_PROFILER_RAISE (assembly_loading, (ass));
2878 mono_assembly_fill_assembly_name (image, &ass->aname);
2880 if (mono_defaults.corlib && strcmp (ass->aname.name, MONO_ASSEMBLY_CORLIB_NAME) == 0) {
2881 // MS.NET doesn't support loading other mscorlibs
2882 g_free (ass);
2883 g_free (base_dir);
2884 mono_image_addref (mono_defaults.corlib);
2885 *status = MONO_IMAGE_OK;
2886 return mono_defaults.corlib->assembly;
2889 /* Add a non-temporary reference because of ass->image */
2890 mono_image_addref (image);
2892 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);
2895 * The load hooks might take locks so we can't call them while holding the
2896 * assemblies lock.
2898 if (ass->aname.name && asmctx != MONO_ASMCTX_INDIVIDUAL) {
2899 /* 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?) */
2900 ass2 = mono_assembly_invoke_search_hook_internal (req->alc, NULL, &ass->aname, asmctx == MONO_ASMCTX_REFONLY, FALSE);
2901 if (ass2) {
2902 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);
2903 g_free (ass);
2904 g_free (base_dir);
2905 mono_image_close (image);
2906 *status = MONO_IMAGE_OK;
2907 return ass2;
2911 /* We need to check for ReferenceAssmeblyAttribute before we
2912 * mark the assembly as loaded and before we fire the load
2913 * hook. Otherwise mono_domain_fire_assembly_load () in
2914 * appdomain.c will cache a mapping from the assembly name to
2915 * this image and we won't be able to look for a different
2916 * candidate. */
2918 if (asmctx != MONO_ASMCTX_REFONLY) {
2919 ERROR_DECL (refasm_error);
2920 if (mono_assembly_has_reference_assembly_attribute (ass, refasm_error)) {
2921 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image for assembly '%s' (%s) has ReferenceAssemblyAttribute, skipping", ass->aname.name, image->name);
2922 g_free (ass);
2923 g_free (base_dir);
2924 mono_image_close (image);
2925 *status = MONO_IMAGE_IMAGE_INVALID;
2926 return NULL;
2928 mono_error_cleanup (refasm_error);
2931 if (predicate && !predicate (ass, user_data)) {
2932 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate returned FALSE, skipping '%s' (%s)\n", ass->aname.name, image->name);
2933 g_free (ass);
2934 g_free (base_dir);
2935 mono_image_close (image);
2936 *status = MONO_IMAGE_IMAGE_INVALID;
2937 return NULL;
2940 mono_assemblies_lock ();
2942 /* If an assembly is loaded into an individual context, always return a
2943 * new MonoAssembly, even if another assembly with the same name has
2944 * already been loaded.
2946 if (image->assembly && asmctx != MONO_ASMCTX_INDIVIDUAL) {
2948 * This means another thread has already loaded the assembly, but not yet
2949 * called the load hooks so the search hook can't find the assembly.
2951 mono_assemblies_unlock ();
2952 ass2 = image->assembly;
2953 g_free (ass);
2954 g_free (base_dir);
2955 mono_image_close (image);
2956 *status = MONO_IMAGE_OK;
2957 return ass2;
2960 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Prepared to set up assembly '%s' (%s)", ass->aname.name, image->name);
2962 /* If asmctx is INDIVIDUAL, image->assembly might not be NULL, so don't
2963 * overwrite it. */
2964 if (image->assembly == NULL)
2965 image->assembly = ass;
2967 loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
2968 mono_assemblies_unlock ();
2970 #ifdef HOST_WIN32
2971 if (m_image_is_module_handle (image))
2972 mono_image_fixup_vtable (image);
2973 #endif
2975 mono_assembly_invoke_load_hook (ass);
2977 MONO_PROFILER_RAISE (assembly_loaded, (ass));
2979 return ass;
2983 * mono_assembly_load_from:
2984 * \param image Image to load the assembly from
2985 * \param fname assembly name to associate with the assembly
2986 * \param status return status code
2988 * If the provided \p image has an assembly reference, it will process the given
2989 * image as an assembly with the given name.
2991 * Most likely you want to use the `api:mono_assembly_load_full` method instead.
2993 * This is equivalent to calling `api:mono_assembly_load_from_full` with the
2994 * \p refonly parameter set to FALSE.
2995 * \returns A valid pointer to a \c MonoAssembly* on success and then \p status will be
2996 * set to \c MONO_IMAGE_OK; or NULL on error.
2998 * If there is an error loading the assembly the \p status will indicate the
2999 * reason with \p status being set to \c MONO_IMAGE_INVALID if the
3000 * image did not contain an assembly reference table.
3003 MonoAssembly *
3004 mono_assembly_load_from (MonoImage *image, const char *fname,
3005 MonoImageOpenStatus *status)
3007 MonoAssembly *res;
3008 MONO_ENTER_GC_UNSAFE;
3009 MonoAssemblyLoadRequest req;
3010 MonoImageOpenStatus def_status;
3011 if (!status)
3012 status = &def_status;
3013 mono_assembly_request_prepare (&req, sizeof (req), MONO_ASMCTX_DEFAULT);
3014 res = mono_assembly_request_load_from (image, fname, &req, status);
3015 MONO_EXIT_GC_UNSAFE;
3016 return res;
3020 * mono_assembly_name_free:
3021 * \param aname assembly name to free
3023 * Frees the provided assembly name object.
3024 * (it does not frees the object itself, only the name members).
3026 void
3027 mono_assembly_name_free (MonoAssemblyName *aname)
3029 MONO_ENTER_GC_UNSAFE;
3030 mono_assembly_name_free_internal (aname);
3031 MONO_EXIT_GC_UNSAFE;
3034 void
3035 mono_assembly_name_free_internal (MonoAssemblyName *aname)
3037 MONO_REQ_GC_UNSAFE_MODE;
3039 if (aname == NULL)
3040 return;
3042 g_free ((void *) aname->name);
3043 g_free ((void *) aname->culture);
3044 g_free ((void *) aname->hash_value);
3045 g_free ((guint8*) aname->public_key);
3048 static gboolean
3049 parse_public_key (const gchar *key, gchar** pubkey, gboolean *is_ecma)
3051 const gchar *pkey;
3052 gchar header [16], val, *arr, *endp;
3053 gint i, j, offset, bitlen, keylen, pkeylen;
3055 //both pubkey and is_ecma are required arguments
3056 g_assert (pubkey && is_ecma);
3058 keylen = strlen (key) >> 1;
3059 if (keylen < 1)
3060 return FALSE;
3062 /* allow the ECMA standard key */
3063 if (strcmp (key, "00000000000000000400000000000000") == 0) {
3064 *pubkey = NULL;
3065 *is_ecma = TRUE;
3066 return TRUE;
3068 *is_ecma = FALSE;
3069 val = g_ascii_xdigit_value (key [0]) << 4;
3070 val |= g_ascii_xdigit_value (key [1]);
3071 switch (val) {
3072 case 0x00:
3073 if (keylen < 13)
3074 return FALSE;
3075 val = g_ascii_xdigit_value (key [24]);
3076 val |= g_ascii_xdigit_value (key [25]);
3077 if (val != 0x06)
3078 return FALSE;
3079 pkey = key + 24;
3080 break;
3081 case 0x06:
3082 pkey = key;
3083 break;
3084 default:
3085 return FALSE;
3088 /* We need the first 16 bytes
3089 * to check whether this key is valid or not */
3090 pkeylen = strlen (pkey) >> 1;
3091 if (pkeylen < 16)
3092 return FALSE;
3094 for (i = 0, j = 0; i < 16; i++) {
3095 header [i] = g_ascii_xdigit_value (pkey [j++]) << 4;
3096 header [i] |= g_ascii_xdigit_value (pkey [j++]);
3099 if (header [0] != 0x06 || /* PUBLICKEYBLOB (0x06) */
3100 header [1] != 0x02 || /* Version (0x02) */
3101 header [2] != 0x00 || /* Reserved (word) */
3102 header [3] != 0x00 ||
3103 (guint)(read32 (header + 8)) != 0x31415352) /* DWORD magic = RSA1 */
3104 return FALSE;
3106 /* Based on this length, we _should_ be able to know if the length is right */
3107 bitlen = read32 (header + 12) >> 3;
3108 if ((bitlen + 16 + 4) != pkeylen)
3109 return FALSE;
3111 arr = (gchar *)g_malloc (keylen + 4);
3112 /* Encode the size of the blob */
3113 mono_metadata_encode_value (keylen, &arr[0], &endp);
3114 offset = (gint)(endp-arr);
3116 for (i = offset, j = 0; i < keylen + offset; i++) {
3117 arr [i] = g_ascii_xdigit_value (key [j++]) << 4;
3118 arr [i] |= g_ascii_xdigit_value (key [j++]);
3121 *pubkey = arr;
3123 return TRUE;
3126 static gboolean
3127 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)
3129 gint len;
3130 gint version_parts;
3131 gchar *pkeyptr, *encoded, tok [8];
3133 memset (aname, 0, sizeof (MonoAssemblyName));
3135 if (version) {
3136 #ifdef ENABLE_NETCORE
3137 int parts [4];
3138 int i;
3139 int part_len;
3141 parts [2] = -1;
3142 parts [3] = -1;
3143 const char *s = version;
3144 version_parts = 0;
3145 for (i = 0; i < 4; ++i) {
3146 int n = sscanf (s, "%u%n", &parts [i], &part_len);
3147 if (n != 1)
3148 return FALSE;
3149 if (parts [i] < 0 || parts [i] > 65535)
3150 return FALSE;
3151 if (i < 2 && parts [i] == 65535)
3152 return FALSE;
3153 version_parts ++;
3154 s += part_len;
3155 if (s [0] == '\0')
3156 break;
3157 if (i < 3) {
3158 if (s [0] != '.')
3159 return FALSE;
3160 s ++;
3163 if (s [0] != '\0')
3164 return FALSE;
3165 if (version_parts < 2 || version_parts > 4)
3166 return FALSE;
3167 aname->major = parts [0];
3168 aname->minor = parts [1];
3169 if (version_parts >= 3)
3170 aname->build = parts [2];
3171 else
3172 aname->build = -1;
3173 if (version_parts == 4)
3174 aname->revision = parts [3];
3175 else
3176 aname->revision = -1;
3177 #else
3178 gint major, minor, build, revision;
3180 version_parts = sscanf (version, "%u.%u.%u.%u", &major, &minor, &build, &revision);
3181 if (version_parts < 2 || version_parts > 4)
3182 return FALSE;
3184 /* FIXME: we should set build & revision to -1 (instead of 0)
3185 if these are not set in the version string. That way, later on,
3186 we can still determine if these were specified. */
3187 aname->major = major;
3188 aname->minor = minor;
3189 if (version_parts >= 3)
3190 aname->build = build;
3191 else
3192 aname->build = 0;
3193 if (version_parts == 4)
3194 aname->revision = revision;
3195 else
3196 aname->revision = 0;
3197 #endif
3200 aname->flags = flags;
3201 aname->arch = arch;
3202 aname->name = g_strdup (name);
3204 if (culture) {
3205 if (g_ascii_strcasecmp (culture, "neutral") == 0)
3206 aname->culture = g_strdup ("");
3207 else
3208 aname->culture = g_strdup (culture);
3211 if (token && strncmp (token, "null", 4) != 0) {
3212 char *lower;
3214 /* the constant includes the ending NULL, hence the -1 */
3215 if (strlen (token) != (MONO_PUBLIC_KEY_TOKEN_LENGTH - 1)) {
3216 mono_assembly_name_free (aname);
3217 return FALSE;
3219 lower = g_ascii_strdown (token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
3220 g_strlcpy ((char*)aname->public_key_token, lower, MONO_PUBLIC_KEY_TOKEN_LENGTH);
3221 g_free (lower);
3224 if (key) {
3225 gboolean is_ecma = FALSE;
3226 gchar *pkey = NULL;
3227 if (strcmp (key, "null") == 0 || !parse_public_key (key, &pkey, &is_ecma)) {
3228 mono_assembly_name_free (aname);
3229 return FALSE;
3232 if (is_ecma) {
3233 g_assert (pkey == NULL);
3234 aname->public_key = NULL;
3235 g_strlcpy ((gchar*)aname->public_key_token, "b77a5c561934e089", MONO_PUBLIC_KEY_TOKEN_LENGTH);
3236 return TRUE;
3239 len = mono_metadata_decode_blob_size ((const gchar *) pkey, (const gchar **) &pkeyptr);
3240 // We also need to generate the key token
3241 mono_digest_get_public_token ((guchar*) tok, (guint8*) pkeyptr, len);
3242 encoded = encode_public_tok ((guchar*) tok, 8);
3243 g_strlcpy ((gchar*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH);
3244 g_free (encoded);
3246 if (save_public_key)
3247 aname->public_key = (guint8*) pkey;
3248 else
3249 g_free (pkey);
3252 return TRUE;
3255 static gboolean
3256 parse_assembly_directory_name (const char *name, const char *dirname, MonoAssemblyName *aname)
3258 gchar **parts;
3259 gboolean res;
3261 parts = g_strsplit (dirname, "_", 3);
3262 if (!parts || !parts[0] || !parts[1] || !parts[2]) {
3263 g_strfreev (parts);
3264 return FALSE;
3267 res = build_assembly_name (name, parts[0], parts[1], parts[2], NULL, 0, 0, aname, FALSE);
3268 g_strfreev (parts);
3269 return res;
3272 static gboolean
3273 split_key_value (const gchar *pair, gchar **key, guint32 *keylen, gchar **value)
3275 char *eqsign = (char*)strchr (pair, '=');
3276 if (!eqsign) {
3277 *key = NULL;
3278 *keylen = 0;
3279 *value = NULL;
3280 return FALSE;
3283 *key = (gchar*)pair;
3284 *keylen = eqsign - *key;
3285 while (*keylen > 0 && g_ascii_isspace ((*key) [*keylen - 1]))
3286 (*keylen)--;
3287 *value = g_strstrip (eqsign + 1);
3288 return TRUE;
3291 gboolean
3292 mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboolean save_public_key, gboolean *is_version_defined, gboolean *is_token_defined)
3294 gchar *dllname;
3295 gchar *dllname_uq;
3296 gchar *version = NULL;
3297 gchar *version_uq;
3298 gchar *culture = NULL;
3299 gchar *culture_uq;
3300 gchar *token = NULL;
3301 gchar *token_uq;
3302 gchar *key = NULL;
3303 gchar *key_uq;
3304 gchar *retargetable = NULL;
3305 gchar *retargetable_uq;
3306 gchar *procarch;
3307 gchar *procarch_uq;
3308 gboolean res;
3309 gchar *value, *part_name;
3310 guint32 part_name_len;
3311 gchar **parts;
3312 gchar **tmp;
3313 gboolean version_defined;
3314 gboolean token_defined;
3315 guint32 flags = 0;
3316 guint32 arch = MONO_PROCESSOR_ARCHITECTURE_NONE;
3318 if (!is_version_defined)
3319 is_version_defined = &version_defined;
3320 *is_version_defined = FALSE;
3321 if (!is_token_defined)
3322 is_token_defined = &token_defined;
3323 *is_token_defined = FALSE;
3325 parts = tmp = g_strsplit (name, ",", 6);
3326 if (!tmp || !*tmp) {
3327 g_strfreev (tmp);
3328 return FALSE;
3331 dllname = g_strstrip (*tmp);
3333 tmp++;
3335 while (*tmp) {
3336 if (!split_key_value (g_strstrip (*tmp), &part_name, &part_name_len, &value))
3337 goto cleanup_and_fail;
3339 if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Version", part_name_len)) {
3340 *is_version_defined = TRUE;
3341 version = value;
3342 if (strlen (version) == 0) {
3343 goto cleanup_and_fail;
3345 tmp++;
3346 continue;
3349 if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Culture", part_name_len)) {
3350 culture = value;
3351 if (strlen (culture) == 0) {
3352 goto cleanup_and_fail;
3354 tmp++;
3355 continue;
3358 if (part_name_len == 14 && !g_ascii_strncasecmp (part_name, "PublicKeyToken", part_name_len)) {
3359 *is_token_defined = TRUE;
3360 token = value;
3361 if (strlen (token) == 0) {
3362 goto cleanup_and_fail;
3364 tmp++;
3365 continue;
3368 if (part_name_len == 9 && !g_ascii_strncasecmp (part_name, "PublicKey", part_name_len)) {
3369 key = value;
3370 if (strlen (key) == 0) {
3371 goto cleanup_and_fail;
3373 tmp++;
3374 continue;
3377 if (part_name_len == 12 && !g_ascii_strncasecmp (part_name, "Retargetable", part_name_len)) {
3378 retargetable = value;
3379 retargetable_uq = unquote (retargetable);
3380 if (retargetable_uq != NULL)
3381 retargetable = retargetable_uq;
3383 if (!g_ascii_strcasecmp (retargetable, "yes")) {
3384 flags |= ASSEMBLYREF_RETARGETABLE_FLAG;
3385 } else if (g_ascii_strcasecmp (retargetable, "no")) {
3386 g_free (retargetable_uq);
3387 goto cleanup_and_fail;
3390 g_free (retargetable_uq);
3391 tmp++;
3392 continue;
3395 if (part_name_len == 21 && !g_ascii_strncasecmp (part_name, "ProcessorArchitecture", part_name_len)) {
3396 procarch = value;
3397 procarch_uq = unquote (procarch);
3398 if (procarch_uq != NULL)
3399 procarch = procarch_uq;
3401 if (!g_ascii_strcasecmp (procarch, "MSIL"))
3402 arch = MONO_PROCESSOR_ARCHITECTURE_MSIL;
3403 else if (!g_ascii_strcasecmp (procarch, "X86"))
3404 arch = MONO_PROCESSOR_ARCHITECTURE_X86;
3405 else if (!g_ascii_strcasecmp (procarch, "IA64"))
3406 arch = MONO_PROCESSOR_ARCHITECTURE_IA64;
3407 else if (!g_ascii_strcasecmp (procarch, "AMD64"))
3408 arch = MONO_PROCESSOR_ARCHITECTURE_AMD64;
3409 else if (!g_ascii_strcasecmp (procarch, "ARM"))
3410 arch = MONO_PROCESSOR_ARCHITECTURE_ARM;
3411 else {
3412 g_free (procarch_uq);
3413 goto cleanup_and_fail;
3416 g_free (procarch_uq);
3417 tmp++;
3418 continue;
3421 g_strfreev (parts);
3422 return FALSE;
3425 /* if retargetable flag is set, then we must have a fully qualified name */
3426 if (retargetable != NULL && (version == NULL || culture == NULL || (key == NULL && token == NULL))) {
3427 goto cleanup_and_fail;
3430 dllname_uq = unquote (dllname);
3431 version_uq = unquote (version);
3432 culture_uq = unquote (culture);
3433 token_uq = unquote (token);
3434 key_uq = unquote (key);
3436 res = build_assembly_name (
3437 dllname_uq == NULL ? dllname : dllname_uq,
3438 version_uq == NULL ? version : version_uq,
3439 culture_uq == NULL ? culture : culture_uq,
3440 token_uq == NULL ? token : token_uq,
3441 key_uq == NULL ? key : key_uq,
3442 flags, arch, aname, save_public_key);
3444 g_free (dllname_uq);
3445 g_free (version_uq);
3446 g_free (culture_uq);
3447 g_free (token_uq);
3448 g_free (key_uq);
3450 g_strfreev (parts);
3451 return res;
3453 cleanup_and_fail:
3454 g_strfreev (parts);
3455 return FALSE;
3458 static char*
3459 unquote (const char *str)
3461 gint slen;
3462 const char *end;
3464 if (str == NULL)
3465 return NULL;
3467 slen = strlen (str);
3468 if (slen < 2)
3469 return NULL;
3471 if (*str != '\'' && *str != '\"')
3472 return NULL;
3474 end = str + slen - 1;
3475 if (*str != *end)
3476 return NULL;
3478 return g_strndup (str + 1, slen - 2);
3482 * mono_assembly_name_parse:
3483 * \param name name to parse
3484 * \param aname the destination assembly name
3486 * Parses an assembly qualified type name and assigns the name,
3487 * version, culture and token to the provided assembly name object.
3489 * \returns TRUE if the name could be parsed.
3491 gboolean
3492 mono_assembly_name_parse (const char *name, MonoAssemblyName *aname)
3494 return mono_assembly_name_parse_full (name, aname, FALSE, NULL, NULL);
3498 * mono_assembly_name_new:
3499 * \param name name to parse
3501 * Allocate a new \c MonoAssemblyName and fill its values from the
3502 * passed \p name.
3504 * \returns a newly allocated structure or NULL if there was any failure.
3506 MonoAssemblyName*
3507 mono_assembly_name_new (const char *name)
3509 MonoAssemblyName *result = NULL;
3510 MONO_ENTER_GC_UNSAFE;
3511 MonoAssemblyName *aname = g_new0 (MonoAssemblyName, 1);
3512 if (mono_assembly_name_parse (name, aname))
3513 result = aname;
3514 else
3515 g_free (aname);
3516 MONO_EXIT_GC_UNSAFE;
3517 return result;
3521 * mono_assembly_name_get_name:
3523 const char*
3524 mono_assembly_name_get_name (MonoAssemblyName *aname)
3526 const char *result = NULL;
3527 MONO_ENTER_GC_UNSAFE;
3528 result = aname->name;
3529 MONO_EXIT_GC_UNSAFE;
3530 return result;
3534 * mono_assembly_name_get_culture:
3536 const char*
3537 mono_assembly_name_get_culture (MonoAssemblyName *aname)
3539 const char *result = NULL;
3540 MONO_ENTER_GC_UNSAFE;
3541 result = aname->culture;
3542 MONO_EXIT_GC_UNSAFE;
3543 return result;
3547 * mono_assembly_name_get_pubkeytoken:
3549 mono_byte*
3550 mono_assembly_name_get_pubkeytoken (MonoAssemblyName *aname)
3552 if (aname->public_key_token [0])
3553 return aname->public_key_token;
3554 return NULL;
3558 * mono_assembly_name_get_version:
3560 uint16_t
3561 mono_assembly_name_get_version (MonoAssemblyName *aname, uint16_t *minor, uint16_t *build, uint16_t *revision)
3563 if (minor)
3564 *minor = aname->minor;
3565 if (build)
3566 *build = aname->build;
3567 if (revision)
3568 *revision = aname->revision;
3569 return aname->major;
3572 static MonoAssembly*
3573 probe_for_partial_name (const char *basepath, const char *fullname, MonoAssemblyName *aname, MonoImageOpenStatus *status)
3575 gchar *fullpath = NULL;
3576 GDir *dirhandle;
3577 const char* direntry;
3578 MonoAssemblyName gac_aname;
3579 gint major=-1, minor=0, build=0, revision=0;
3580 gboolean exact_version;
3582 dirhandle = g_dir_open (basepath, 0, NULL);
3583 if (!dirhandle)
3584 return NULL;
3586 exact_version = (aname->major | aname->minor | aname->build | aname->revision) != 0;
3588 while ((direntry = g_dir_read_name (dirhandle))) {
3589 gboolean match = TRUE;
3591 if(!parse_assembly_directory_name (aname->name, direntry, &gac_aname))
3592 continue;
3594 if (aname->culture != NULL && strcmp (aname->culture, gac_aname.culture) != 0)
3595 match = FALSE;
3597 if (match && strlen ((char*)aname->public_key_token) > 0 &&
3598 !mono_public_tokens_are_equal (aname->public_key_token, gac_aname.public_key_token))
3599 match = FALSE;
3601 if (match) {
3602 if (exact_version) {
3603 match = (aname->major == gac_aname.major && aname->minor == gac_aname.minor &&
3604 aname->build == gac_aname.build && aname->revision == gac_aname.revision);
3606 else if (gac_aname.major < major)
3607 match = FALSE;
3608 else if (gac_aname.major == major) {
3609 if (gac_aname.minor < minor)
3610 match = FALSE;
3611 else if (gac_aname.minor == minor) {
3612 if (gac_aname.build < build)
3613 match = FALSE;
3614 else if (gac_aname.build == build && gac_aname.revision <= revision)
3615 match = FALSE;
3620 if (match) {
3621 major = gac_aname.major;
3622 minor = gac_aname.minor;
3623 build = gac_aname.build;
3624 revision = gac_aname.revision;
3625 g_free (fullpath);
3626 fullpath = g_build_path (G_DIR_SEPARATOR_S, basepath, direntry, fullname, NULL);
3629 mono_assembly_name_free (&gac_aname);
3632 g_dir_close (dirhandle);
3634 if (fullpath == NULL)
3635 return NULL;
3636 else {
3637 MonoAssemblyOpenRequest req;
3638 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
3639 MonoAssembly *res = mono_assembly_request_open (fullpath, &req, status);
3640 g_free (fullpath);
3641 return res;
3646 * mono_assembly_load_with_partial_name:
3647 * \param name an assembly name that is then parsed by `api:mono_assembly_name_parse`.
3648 * \param status return status code
3650 * Loads a \c MonoAssembly from a name. The name is parsed using `api:mono_assembly_name_parse`,
3651 * so it might contain a qualified type name, version, culture and token.
3653 * This will load the assembly from the file whose name is derived from the assembly name
3654 * by appending the \c .dll extension.
3656 * The assembly is loaded from either one of the extra Global Assembly Caches specified
3657 * by the extra GAC paths (specified by the \c MONO_GAC_PREFIX environment variable) or
3658 * if that fails from the GAC.
3660 * \returns NULL on failure, or a pointer to a \c MonoAssembly on success.
3662 MonoAssembly*
3663 mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status)
3665 MonoAssembly *result;
3666 MONO_ENTER_GC_UNSAFE;
3667 MonoImageOpenStatus def_status;
3668 if (!status)
3669 status = &def_status;
3670 result = mono_assembly_load_with_partial_name_internal (name, status);
3671 MONO_EXIT_GC_UNSAFE;
3672 return result;
3675 MonoAssembly*
3676 mono_assembly_load_with_partial_name_internal (const char *name, MonoImageOpenStatus *status)
3678 ERROR_DECL (error);
3679 MonoAssembly *res;
3680 MonoAssemblyName *aname, base_name;
3681 MonoAssemblyName mapped_aname;
3683 MONO_REQ_GC_UNSAFE_MODE;
3685 g_assert (status != NULL);
3687 memset (&base_name, 0, sizeof (MonoAssemblyName));
3688 aname = &base_name;
3690 if (!mono_assembly_name_parse (name, aname))
3691 return NULL;
3694 * If no specific version has been requested, make sure we load the
3695 * correct version for system assemblies.
3697 if ((aname->major | aname->minor | aname->build | aname->revision) == 0)
3698 aname = mono_assembly_remap_version (aname, &mapped_aname);
3700 /* FIXME: get the ALC as an argument from the caller */
3701 MonoAssemblyLoadContext *alc = mono_domain_ambient_alc (mono_domain_get ());
3703 res = mono_assembly_loaded_internal (alc, aname, FALSE);
3704 if (res) {
3705 mono_assembly_name_free (aname);
3706 return res;
3709 res = invoke_assembly_preload_hook (alc, aname, assemblies_path);
3710 if (res) {
3711 res->in_gac = FALSE;
3712 mono_assembly_name_free (aname);
3713 return res;
3716 #ifndef DISABLE_GAC
3717 gchar *fullname, *gacpath;
3718 gchar **paths;
3719 fullname = g_strdup_printf ("%s.dll", aname->name);
3721 if (extra_gac_paths) {
3722 paths = extra_gac_paths;
3723 while (!res && *paths) {
3724 gacpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", aname->name, NULL);
3725 res = probe_for_partial_name (gacpath, fullname, aname, status);
3726 g_free (gacpath);
3727 paths++;
3731 if (res) {
3732 res->in_gac = TRUE;
3733 g_free (fullname);
3734 mono_assembly_name_free (aname);
3735 return res;
3738 gacpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), "mono", "gac", aname->name, NULL);
3739 res = probe_for_partial_name (gacpath, fullname, aname, status);
3740 g_free (gacpath);
3742 g_free (fullname);
3743 if (res)
3744 res->in_gac = TRUE;
3745 #endif
3747 mono_assembly_name_free (aname);
3749 if (!res) {
3750 MonoDomain *domain = mono_domain_get ();
3752 res = mono_try_assembly_resolve (domain, name, NULL, FALSE, error);
3753 if (!is_ok (error)) {
3754 mono_error_cleanup (error);
3755 if (*status == MONO_IMAGE_OK)
3756 *status = MONO_IMAGE_IMAGE_INVALID;
3760 return res;
3763 static MonoBoolean
3764 mono_assembly_is_in_gac (const gchar *filename)
3766 #ifndef DISABLE_GAC
3767 const gchar *rootdir;
3768 gchar *gp;
3769 gchar **paths;
3771 if (filename == NULL)
3772 return FALSE;
3774 for (paths = extra_gac_paths; paths && *paths; paths++) {
3775 if (strstr (*paths, filename) != *paths)
3776 continue;
3778 gp = (gchar *) (filename + strlen (*paths));
3779 if (*gp != G_DIR_SEPARATOR)
3780 continue;
3781 gp++;
3782 if (strncmp (gp, "lib", 3))
3783 continue;
3784 gp += 3;
3785 if (*gp != G_DIR_SEPARATOR)
3786 continue;
3787 gp++;
3788 if (strncmp (gp, "mono", 4))
3789 continue;
3790 gp += 4;
3791 if (*gp != G_DIR_SEPARATOR)
3792 continue;
3793 gp++;
3794 if (strncmp (gp, "gac", 3))
3795 continue;
3796 gp += 3;
3797 if (*gp != G_DIR_SEPARATOR)
3798 continue;
3800 return TRUE;
3803 rootdir = mono_assembly_getrootdir ();
3804 if (strstr (filename, rootdir) != filename)
3805 return FALSE;
3807 gp = (gchar *) (filename + strlen (rootdir));
3808 if (*gp != G_DIR_SEPARATOR)
3809 return FALSE;
3810 gp++;
3811 if (strncmp (gp, "mono", 4))
3812 return FALSE;
3813 gp += 4;
3814 if (*gp != G_DIR_SEPARATOR)
3815 return FALSE;
3816 gp++;
3817 if (strncmp (gp, "gac", 3))
3818 return FALSE;
3819 gp += 3;
3820 if (*gp != G_DIR_SEPARATOR)
3821 return FALSE;
3822 return TRUE;
3823 #else
3824 return FALSE;
3825 #endif /* DISABLE_GAC */
3828 static MonoImage*
3829 mono_assembly_load_publisher_policy (MonoAssemblyName *aname)
3831 MonoImage *image = NULL;
3832 #ifndef DISABLE_GAC
3833 gchar *filename, *pname, *name, *culture, *version, *fullpath, *subpath;
3834 gchar **paths;
3835 gint32 len;
3837 if (strstr (aname->name, ".dll")) {
3838 len = strlen (aname->name) - 4;
3839 name = (gchar *)g_malloc (len + 1);
3840 memcpy (name, aname->name, len);
3841 name[len] = 0;
3842 } else
3843 name = g_strdup (aname->name);
3845 if (aname->culture)
3846 culture = g_utf8_strdown (aname->culture, -1);
3847 else
3848 culture = g_strdup ("");
3850 pname = g_strdup_printf ("policy.%d.%d.%s", aname->major, aname->minor, name);
3851 version = g_strdup_printf ("0.0.0.0_%s_%s", culture, aname->public_key_token);
3852 g_free (name);
3853 g_free (culture);
3855 filename = g_strconcat (pname, ".dll", NULL);
3856 subpath = g_build_path (G_DIR_SEPARATOR_S, pname, version, filename, NULL);
3857 g_free (pname);
3858 g_free (version);
3859 g_free (filename);
3861 image = NULL;
3862 if (extra_gac_paths) {
3863 paths = extra_gac_paths;
3864 while (!image && *paths) {
3865 fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths,
3866 "lib", "mono", "gac", subpath, NULL);
3867 image = mono_image_open (fullpath, NULL);
3868 g_free (fullpath);
3869 paths++;
3873 if (image) {
3874 g_free (subpath);
3875 return image;
3878 fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
3879 "mono", "gac", subpath, NULL);
3880 image = mono_image_open (fullpath, NULL);
3881 g_free (subpath);
3882 g_free (fullpath);
3883 #endif
3885 return image;
3888 static MonoAssemblyName*
3889 mono_assembly_bind_version (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname, MonoAssemblyName *dest_name)
3891 memcpy (dest_name, aname, sizeof (MonoAssemblyName));
3892 dest_name->major = info->new_version.major;
3893 dest_name->minor = info->new_version.minor;
3894 dest_name->build = info->new_version.build;
3895 dest_name->revision = info->new_version.revision;
3897 return dest_name;
3900 /* LOCKING: assembly_binding lock must be held */
3901 static MonoAssemblyBindingInfo*
3902 search_binding_loaded (MonoAssemblyName *aname)
3904 GSList *tmp;
3906 for (tmp = loaded_assembly_bindings; tmp; tmp = tmp->next) {
3907 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)tmp->data;
3908 if (assembly_binding_maps_name (info, aname))
3909 return info;
3912 return NULL;
3915 static inline gboolean
3916 info_compare_versions (AssemblyVersionSet *left, AssemblyVersionSet *right)
3918 if (left->major != right->major || left->minor != right->minor ||
3919 left->build != right->build || left->revision != right->revision)
3920 return FALSE;
3922 return TRUE;
3925 static inline gboolean
3926 info_versions_equal (MonoAssemblyBindingInfo *left, MonoAssemblyBindingInfo *right)
3928 if (left->has_old_version_bottom != right->has_old_version_bottom)
3929 return FALSE;
3931 if (left->has_old_version_top != right->has_old_version_top)
3932 return FALSE;
3934 if (left->has_new_version != right->has_new_version)
3935 return FALSE;
3937 if (left->has_old_version_bottom && !info_compare_versions (&left->old_version_bottom, &right->old_version_bottom))
3938 return FALSE;
3940 if (left->has_old_version_top && !info_compare_versions (&left->old_version_top, &right->old_version_top))
3941 return FALSE;
3943 if (left->has_new_version && !info_compare_versions (&left->new_version, &right->new_version))
3944 return FALSE;
3946 return TRUE;
3949 /* LOCKING: assumes all the necessary locks are held */
3950 static void
3951 assembly_binding_info_parsed (MonoAssemblyBindingInfo *info, void *user_data)
3953 MonoAssemblyBindingInfo *info_copy;
3954 GSList *tmp;
3955 MonoAssemblyBindingInfo *info_tmp;
3956 MonoDomain *domain = (MonoDomain*)user_data;
3958 if (!domain)
3959 return;
3961 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)) {
3962 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Discarding assembly binding to problematic version %s v%d.%d.%d.%d",
3963 info->name, info->new_version.major, info->new_version.minor, info->new_version.build, info->new_version.revision);
3964 return;
3967 for (tmp = domain->assembly_bindings; tmp; tmp = tmp->next) {
3968 info_tmp = (MonoAssemblyBindingInfo *)tmp->data;
3969 if (strcmp (info->name, info_tmp->name) == 0 && info_versions_equal (info, info_tmp))
3970 return;
3973 info_copy = (MonoAssemblyBindingInfo *)mono_mempool_alloc0 (domain->mp, sizeof (MonoAssemblyBindingInfo));
3974 memcpy (info_copy, info, sizeof (MonoAssemblyBindingInfo));
3975 if (info->name)
3976 info_copy->name = mono_mempool_strdup (domain->mp, info->name);
3977 if (info->culture)
3978 info_copy->culture = mono_mempool_strdup (domain->mp, info->culture);
3980 domain->assembly_bindings = g_slist_append_mempool (domain->mp, domain->assembly_bindings, info_copy);
3983 static int
3984 get_version_number (int major, int minor)
3986 return major * 256 + minor;
3989 static inline gboolean
3990 info_major_minor_in_range (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname)
3992 int aname_version_number = get_version_number (aname->major, aname->minor);
3993 if (!info->has_old_version_bottom)
3994 return FALSE;
3996 if (get_version_number (info->old_version_bottom.major, info->old_version_bottom.minor) > aname_version_number)
3997 return FALSE;
3999 if (info->has_old_version_top && get_version_number (info->old_version_top.major, info->old_version_top.minor) < aname_version_number)
4000 return FALSE;
4002 /* This is not the nicest way to do it, but it's a by-product of the way parsing is done */
4003 info->major = aname->major;
4004 info->minor = aname->minor;
4006 return TRUE;
4009 /* LOCKING: Assumes that we are already locked - both loader and domain locks */
4010 static MonoAssemblyBindingInfo*
4011 get_per_domain_assembly_binding_info (MonoDomain *domain, MonoAssemblyName *aname)
4013 MonoAssemblyBindingInfo *info;
4014 GSList *list;
4016 if (!domain->assembly_bindings)
4017 return NULL;
4019 info = NULL;
4020 for (list = domain->assembly_bindings; list; list = list->next) {
4021 info = (MonoAssemblyBindingInfo *)list->data;
4022 if (info && !strcmp (aname->name, info->name) && info_major_minor_in_range (info, aname))
4023 break;
4024 info = NULL;
4027 if (info) {
4028 if (info->name && info->public_key_token [0] && info->has_old_version_bottom &&
4029 info->has_new_version && assembly_binding_maps_name (info, aname))
4030 info->is_valid = TRUE;
4031 else
4032 info->is_valid = FALSE;
4035 return info;
4038 void
4039 mono_domain_parse_assembly_bindings (MonoDomain *domain, int amajor, int aminor, gchar *domain_config_file_name)
4041 if (domain->assembly_bindings_parsed)
4042 return;
4043 mono_domain_lock (domain);
4044 if (!domain->assembly_bindings_parsed) {
4046 gchar *domain_config_file_path = mono_portability_find_file (domain_config_file_name, TRUE);
4048 if (!domain_config_file_path)
4049 domain_config_file_path = domain_config_file_name;
4051 mono_config_parse_assembly_bindings (domain_config_file_path, amajor, aminor, domain, assembly_binding_info_parsed);
4052 domain->assembly_bindings_parsed = TRUE;
4053 if (domain_config_file_name != domain_config_file_path)
4054 g_free (domain_config_file_path);
4057 mono_domain_unlock (domain);
4060 static MonoAssemblyName*
4061 mono_assembly_apply_binding (MonoAssemblyName *aname, MonoAssemblyName *dest_name)
4063 HANDLE_FUNCTION_ENTER ();
4065 ERROR_DECL (error);
4066 MonoAssemblyBindingInfo *info, *info2;
4067 MonoImage *ppimage;
4068 MonoDomain *domain;
4070 if (aname->public_key_token [0] == 0)
4071 goto return_aname;
4073 domain = mono_domain_get ();
4075 mono_assembly_binding_lock ();
4076 info = search_binding_loaded (aname);
4077 mono_assembly_binding_unlock ();
4079 if (!info) {
4080 mono_domain_lock (domain);
4081 info = get_per_domain_assembly_binding_info (domain, aname);
4082 mono_domain_unlock (domain);
4085 if (info) {
4086 if (!check_policy_versions (info, aname))
4087 goto return_aname;
4089 mono_assembly_bind_version (info, aname, dest_name);
4090 goto return_dest_name;
4093 MonoAppDomainSetupHandle setup;
4094 MonoStringHandle configuration_file;
4096 if (domain
4097 && !MONO_HANDLE_IS_NULL (setup = MONO_HANDLE_NEW (MonoAppDomainSetup, domain->setup))
4098 && !MONO_HANDLE_IS_NULL (configuration_file = MONO_HANDLE_NEW_GET (MonoString, setup, configuration_file))) {
4099 char *domain_config_file_name = mono_string_handle_to_utf8 (configuration_file, error);
4100 /* expect this to succeed because mono_domain_set_options_from_config () did
4101 * the same thing when the domain was created. */
4102 mono_error_assert_ok (error);
4103 mono_domain_parse_assembly_bindings (domain, aname->major, aname->minor, domain_config_file_name);
4104 g_free (domain_config_file_name);
4106 mono_domain_lock (domain);
4107 info2 = get_per_domain_assembly_binding_info (domain, aname);
4109 if (info2) {
4110 info = (MonoAssemblyBindingInfo *)g_memdup (info2, sizeof (MonoAssemblyBindingInfo));
4111 info->name = g_strdup (info2->name);
4112 info->culture = g_strdup (info2->culture);
4113 info->domain_id = domain->domain_id;
4116 mono_domain_unlock (domain);
4119 if (!info) {
4120 info = g_new0 (MonoAssemblyBindingInfo, 1);
4121 info->major = aname->major;
4122 info->minor = aname->minor;
4125 if (!info->is_valid) {
4126 ppimage = mono_assembly_load_publisher_policy (aname);
4127 if (ppimage) {
4128 get_publisher_policy_info (ppimage, aname, info);
4129 mono_image_close (ppimage);
4133 /* Define default error value if needed */
4134 if (!info->is_valid) {
4135 info->name = g_strdup (aname->name);
4136 info->culture = g_strdup (aname->culture);
4137 g_strlcpy ((char *)info->public_key_token, (const char *)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
4140 mono_assembly_binding_lock ();
4141 info2 = search_binding_loaded (aname);
4142 if (info2) {
4143 /* This binding was added by another thread
4144 * before us */
4145 mono_assembly_binding_info_free (info);
4146 g_free (info);
4148 info = info2;
4149 } else
4150 loaded_assembly_bindings = g_slist_prepend (loaded_assembly_bindings, info);
4152 mono_assembly_binding_unlock ();
4154 if (!info->is_valid || !check_policy_versions (info, aname))
4155 goto return_aname;
4157 mono_assembly_bind_version (info, aname, dest_name);
4158 goto return_dest_name;
4160 MonoAssemblyName* result;
4162 return_dest_name:
4163 result = dest_name;
4164 goto exit;
4166 return_aname:
4167 result = aname;
4168 goto exit;
4169 exit:
4170 HANDLE_FUNCTION_RETURN_VAL (result);
4173 #ifndef DISABLE_GAC
4175 * mono_assembly_load_from_gac
4177 * \param aname The assembly name object
4179 static MonoAssembly*
4180 mono_assembly_load_from_gac (MonoAssemblyName *aname, gchar *filename, MonoImageOpenStatus *status, MonoBoolean refonly)
4182 MonoAssembly *result = NULL;
4183 gchar *name, *version, *culture, *fullpath, *subpath;
4184 gint32 len;
4185 gchar **paths;
4186 char *pubtok;
4188 if (aname->public_key_token [0] == 0) {
4189 return NULL;
4192 if (strstr (aname->name, ".dll")) {
4193 len = strlen (filename) - 4;
4194 name = (gchar *)g_malloc (len + 1);
4195 memcpy (name, aname->name, len);
4196 name[len] = 0;
4197 } else {
4198 name = g_strdup (aname->name);
4201 if (aname->culture) {
4202 culture = g_utf8_strdown (aname->culture, -1);
4203 } else {
4204 culture = g_strdup ("");
4207 pubtok = g_ascii_strdown ((char*)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
4208 version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major,
4209 aname->minor, aname->build, aname->revision,
4210 culture, pubtok);
4211 g_free (pubtok);
4213 subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL);
4214 g_free (name);
4215 g_free (version);
4216 g_free (culture);
4218 MonoAssemblyOpenRequest req;
4219 mono_assembly_request_prepare (&req.request, sizeof (req), refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT);
4221 if (extra_gac_paths) {
4222 paths = extra_gac_paths;
4223 while (!result && *paths) {
4224 fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", subpath, NULL);
4225 result = mono_assembly_request_open (fullpath, &req, status);
4226 g_free (fullpath);
4227 paths++;
4231 if (result) {
4232 result->in_gac = TRUE;
4233 g_free (subpath);
4234 return result;
4237 fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
4238 "mono", "gac", subpath, NULL);
4239 result = mono_assembly_request_open (fullpath, &req, status);
4240 g_free (fullpath);
4242 if (result)
4243 result->in_gac = TRUE;
4245 g_free (subpath);
4247 return result;
4249 #endif /* DISABLE_GAC */
4251 MonoAssembly*
4252 mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *status)
4254 MonoAssemblyName *aname;
4255 MonoAssemblyOpenRequest req;
4256 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
4258 if (corlib) {
4259 /* g_print ("corlib already loaded\n"); */
4260 return corlib;
4263 req.request.alc = mono_domain_default_alc (mono_domain_get ());
4265 #ifdef ENABLE_NETCORE
4266 aname = mono_assembly_name_new (MONO_ASSEMBLY_CORLIB_NAME);
4267 corlib = invoke_assembly_preload_hook (req.request.alc, aname, NULL);
4268 /* MonoCore preload hook should know how to find it */
4269 /* FIXME: AOT compiler comes here without an installed hook. */
4270 if (!corlib) {
4271 if (assemblies_path) { // Custom assemblies path set via MONO_PATH or mono_set_assemblies_path
4272 char *corlib_name = g_strdup_printf ("%s.dll", MONO_ASSEMBLY_CORLIB_NAME);
4273 corlib = load_in_path (corlib_name, (const char**)assemblies_path, &req, status);
4276 g_assert (corlib);
4277 #else
4278 // A nonstandard preload hook may provide a special mscorlib assembly
4279 aname = mono_assembly_name_new ("mscorlib.dll");
4280 corlib = invoke_assembly_preload_hook (req.request.alc, aname, assemblies_path);
4281 mono_assembly_name_free (aname);
4282 g_free (aname);
4283 if (corlib != NULL)
4284 goto return_corlib_and_facades;
4286 // This unusual directory layout can occur if mono is being built and run out of its own source repo
4287 if (assemblies_path) { // Custom assemblies path set via MONO_PATH or mono_set_assemblies_path
4288 corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, &req, status);
4289 if (corlib)
4290 goto return_corlib_and_facades;
4293 /* Normal case: Load corlib from mono/<version> */
4294 char *corlib_file;
4295 corlib_file = g_build_filename ("mono", runtime->framework_version, "mscorlib.dll", NULL);
4296 if (assemblies_path) { // Custom assemblies path
4297 corlib = load_in_path (corlib_file, (const char**)assemblies_path, &req, status);
4298 if (corlib) {
4299 g_free (corlib_file);
4300 goto return_corlib_and_facades;
4303 corlib = load_in_path (corlib_file, (const char**) default_path, &req, status);
4304 g_free (corlib_file);
4306 return_corlib_and_facades:
4307 if (corlib) // FIXME: stop hardcoding 4.5 here
4308 default_path [1] = g_strdup_printf ("%s/Facades", corlib->basedir);
4309 #endif /*!ENABLE_NETCORE*/
4311 return corlib;
4314 static MonoAssembly*
4315 prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly)
4317 ERROR_DECL (refasm_error);
4318 if (candidate && !refonly) {
4319 /* .NET Framework seems to not check for ReferenceAssemblyAttribute on dynamic assemblies */
4320 if (!image_is_dynamic (candidate->image) &&
4321 mono_assembly_has_reference_assembly_attribute (candidate, refasm_error))
4322 candidate = NULL;
4324 mono_error_cleanup (refasm_error);
4325 return candidate;
4328 gboolean
4329 mono_assembly_candidate_predicate_sn_same_name (MonoAssembly *candidate, gpointer ud)
4331 MonoAssemblyName *wanted_name = (MonoAssemblyName*)ud;
4332 MonoAssemblyName *candidate_name = &candidate->aname;
4334 g_assert (wanted_name != NULL);
4335 g_assert (candidate_name != NULL);
4337 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
4338 char * s = mono_stringify_assembly_name (wanted_name);
4339 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: wanted = %s\n", s);
4340 g_free (s);
4341 s = mono_stringify_assembly_name (candidate_name);
4342 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate = %s\n", s);
4343 g_free (s);
4347 /* Wanted name has no token, not strongly named: always matches. */
4348 if (0 == wanted_name->public_key_token [0]) {
4349 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: wanted has no token, returning TRUE\n");
4350 return TRUE;
4353 /* Candidate name has no token, not strongly named: never matches */
4354 if (0 == candidate_name->public_key_token [0]) {
4355 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate has no token, returning FALSE\n");
4356 return FALSE;
4359 return exact_sn_match (wanted_name, candidate_name) ||
4360 framework_assembly_sn_match (wanted_name, candidate_name);
4363 gboolean
4364 exact_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name)
4366 #if ENABLE_NETCORE
4367 gboolean result = mono_assembly_names_equal_flags (wanted_name, candidate_name, MONO_ANAME_EQ_IGNORE_VERSION | MONO_ANAME_EQ_IGNORE_PUBKEY);
4368 if (result && assembly_names_compare_versions (wanted_name, candidate_name, -1) > 0)
4369 result = FALSE;
4370 #else
4371 gboolean result = mono_assembly_names_equal (wanted_name, candidate_name);
4372 #endif
4374 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate and wanted names %s\n",
4375 result ? "match, returning TRUE" : "don't match, returning FALSE");
4376 return result;
4380 gboolean
4381 framework_assembly_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name)
4383 #ifndef DISABLE_DESKTOP_LOADER
4384 g_assert (wanted_name != NULL);
4385 g_assert (candidate_name != NULL);
4386 const AssemblyVersionMap *vmap = (AssemblyVersionMap *)g_hash_table_lookup (assembly_remapping_table, wanted_name->name);
4387 if (vmap) {
4388 if (!vmap->framework_facade_assembly) {
4389 /* 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. */
4390 gboolean result = mono_assembly_names_equal_flags (wanted_name, candidate_name, MONO_ANAME_EQ_IGNORE_PUBKEY);
4391 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");
4392 return result;
4393 } else {
4394 /* For facades, the name and public key token should
4395 * match, but the version doesn't matter as long as the
4396 * candidate is not older. */
4397 gboolean result = mono_assembly_names_equal_flags (wanted_name, candidate_name, MONO_ANAME_EQ_IGNORE_VERSION);
4398 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate and wanted names %s (ignoring version)", result ? "match" : "don't match, returning FALSE");
4399 if (result) {
4400 // compare major of candidate and wanted
4401 int c = assembly_names_compare_versions (candidate_name, wanted_name, 1);
4402 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"),
4403 (c >= 0) ? "TRUE" : "FALSE");
4404 return (c >= 0); // don't accept a candidate that's older than wanted.
4405 } else {
4406 return FALSE;
4410 #endif
4411 return FALSE;
4414 static MonoAssembly*
4415 mono_assembly_request_byname_nosearch (MonoAssemblyName *aname,
4416 const MonoAssemblyByNameRequest *req,
4417 MonoImageOpenStatus *status)
4419 MonoAssembly *result;
4420 MonoAssemblyName maped_aname;
4421 MonoAssemblyName maped_name_pp;
4423 aname = mono_assembly_remap_version (aname, &maped_aname);
4425 const gboolean refonly = req->request.asmctx == MONO_ASMCTX_REFONLY;
4427 /* Reflection only assemblies don't get assembly binding */
4428 if (!refonly)
4429 aname = mono_assembly_apply_binding (aname, &maped_name_pp);
4431 result = mono_assembly_loaded_internal (req->request.alc, aname, refonly);
4432 if (result)
4433 return result;
4435 result = refonly ? invoke_assembly_refonly_preload_hook (req->request.alc, aname, assemblies_path) : invoke_assembly_preload_hook (req->request.alc, aname, assemblies_path);
4436 if (result) {
4437 result->in_gac = FALSE;
4438 return result;
4441 /* TODO: for netcore can we avoid calling this method? There is no GAC and we also shouldn't need to load anything from the default path */
4442 return mono_assembly_load_full_gac_base_default (aname, req->basedir, req->request.alc, req->request.asmctx, status);
4445 /* Like mono_assembly_request_byname_nosearch, but don't ask the preload look (ie,
4446 * the appdomain) to run. Just looks in the gac, the specified base dir or the
4447 * default_path. Does NOT look in the appdomain application base or in the
4448 * MONO_PATH.
4450 MonoAssembly*
4451 mono_assembly_load_full_gac_base_default (MonoAssemblyName *aname,
4452 const char *basedir,
4453 MonoAssemblyLoadContext *alc,
4454 MonoAssemblyContextKind asmctx,
4455 MonoImageOpenStatus *status)
4457 MonoAssembly *result;
4458 MonoAssemblyName maped_aname;
4459 char *fullpath, *filename;
4460 int ext_index;
4461 const char *ext;
4462 int len;
4464 /* If we remap e.g. 4.1.3.0 to 4.0.0.0, look in the 4.0.0.0
4465 * GAC directory, not 4.1.3.0 */
4466 aname = mono_assembly_remap_version (aname, &maped_aname);
4468 /* Currently we retrieve the loaded corlib for reflection
4469 * only requests, like a common reflection only assembly
4471 gboolean name_is_corlib = strcmp (aname->name, MONO_ASSEMBLY_CORLIB_NAME) == 0;
4472 /* Assembly.Load (new AssemblyName ("mscorlib.dll")) (respectively,
4473 * "System.Private.CoreLib.dll" for netcore) is treated the same as
4474 * "mscorlib" (resp "System.Private.CoreLib"). */
4475 name_is_corlib = name_is_corlib || strcmp (aname->name, MONO_ASSEMBLY_CORLIB_NAME ".dll") == 0;
4476 if (name_is_corlib) {
4477 return mono_assembly_load_corlib (mono_get_runtime_info (), status);
4480 MonoAssemblyCandidatePredicate predicate = NULL;
4481 void* predicate_ud = NULL;
4482 #if !defined(DISABLE_DESKTOP_LOADER)
4483 if (G_LIKELY (mono_loader_get_strict_strong_names ())) {
4484 predicate = &mono_assembly_candidate_predicate_sn_same_name;
4485 predicate_ud = aname;
4487 #endif
4489 MonoAssemblyOpenRequest req;
4490 mono_assembly_request_prepare (&req.request, sizeof (req), asmctx);
4491 req.request.alc = alc;
4492 req.request.predicate = predicate;
4493 req.request.predicate_ud = predicate_ud;
4495 len = strlen (aname->name);
4496 for (ext_index = 0; ext_index < 2; ext_index ++) {
4497 ext = ext_index == 0 ? ".dll" : ".exe";
4498 if (len > 4 && (!strcmp (aname->name + len - 4, ".dll") || !strcmp (aname->name + len - 4, ".exe"))) {
4499 filename = g_strdup (aname->name);
4500 /* Don't try appending .dll/.exe if it already has one of those extensions */
4501 ext_index++;
4502 } else {
4503 filename = g_strconcat (aname->name, ext, NULL);
4506 #ifndef DISABLE_GAC
4507 const gboolean refonly = asmctx == MONO_ASMCTX_REFONLY;
4509 result = mono_assembly_load_from_gac (aname, filename, status, refonly);
4510 if (result) {
4511 g_free (filename);
4512 return result;
4514 #endif
4516 if (basedir) {
4517 fullpath = g_build_filename (basedir, filename, NULL);
4518 result = mono_assembly_request_open (fullpath, &req, status);
4519 g_free (fullpath);
4520 if (result) {
4521 result->in_gac = FALSE;
4522 g_free (filename);
4523 return result;
4527 result = load_in_path (filename, (const char**) default_path, &req, status);
4528 if (result)
4529 result->in_gac = FALSE;
4530 g_free (filename);
4531 if (result)
4532 return result;
4535 return result;
4538 MonoAssembly*
4539 mono_assembly_request_byname (MonoAssemblyName *aname, const MonoAssemblyByNameRequest *req, MonoImageOpenStatus *status)
4541 MonoDomain *domain = mono_domain_get ();
4542 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Request to load %s in (domain %p, alc %p)", aname->name, domain, (gpointer)req->request.alc);
4543 MonoAssembly *result = mono_assembly_request_byname_nosearch (aname, req, status);
4544 const gboolean refonly = req->request.asmctx == MONO_ASMCTX_REFONLY;
4546 if (!result && !req->no_postload_search) {
4547 /* Try a postload search hook */
4548 result = mono_assembly_invoke_search_hook_internal (req->request.alc, req->requesting_assembly, aname, refonly, TRUE);
4549 result = prevent_reference_assembly_from_running (result, refonly);
4551 return result;
4555 * mono_assembly_load_full:
4556 * \param aname A MonoAssemblyName with the assembly name to load.
4557 * \param basedir A directory to look up the assembly at.
4558 * \param status a pointer to a MonoImageOpenStatus to return the status of the load operation
4559 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
4561 * Loads the assembly referenced by \p aname, if the value of \p basedir is not NULL, it
4562 * attempts to load the assembly from that directory before probing the standard locations.
4564 * If the assembly is being opened in reflection-only mode (\p refonly set to TRUE) then no
4565 * assembly binding takes place.
4567 * \returns the assembly referenced by \p aname loaded or NULL on error. On error the
4568 * value pointed by \p status is updated with an error code.
4570 MonoAssembly*
4571 mono_assembly_load_full (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status, gboolean refonly)
4573 MonoAssembly *res;
4574 MONO_ENTER_GC_UNSAFE;
4575 MonoAssemblyByNameRequest req;
4576 mono_assembly_request_prepare (&req.request, sizeof (req), refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT);
4577 req.requesting_assembly = NULL;
4578 req.basedir = basedir;
4579 res = mono_assembly_request_byname (aname, &req, status);
4580 MONO_EXIT_GC_UNSAFE;
4581 return res;
4585 * mono_assembly_load:
4586 * \param aname A MonoAssemblyName with the assembly name to load.
4587 * \param basedir A directory to look up the assembly at.
4588 * \param status a pointer to a MonoImageOpenStatus to return the status of the load operation
4590 * Loads the assembly referenced by \p aname, if the value of \p basedir is not NULL, it
4591 * attempts to load the assembly from that directory before probing the standard locations.
4593 * \returns the assembly referenced by \p aname loaded or NULL on error. On error the
4594 * value pointed by \p status is updated with an error code.
4596 MonoAssembly*
4597 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
4599 MonoAssemblyByNameRequest req;
4600 mono_assembly_request_prepare (&req.request, sizeof (req), MONO_ASMCTX_DEFAULT);
4601 req.requesting_assembly = NULL;
4602 req.request.alc = mono_domain_default_alc (mono_domain_get ());
4603 req.basedir = basedir;
4604 return mono_assembly_request_byname (aname, &req, status);
4608 * mono_assembly_loaded_full:
4609 * \param aname an assembly to look for.
4610 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
4612 * This is used to determine if the specified assembly has been loaded
4613 * \returns NULL If the given \p aname assembly has not been loaded, or a pointer to
4614 * a \c MonoAssembly that matches the \c MonoAssemblyName specified.
4616 MonoAssembly*
4617 mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly)
4619 MonoAssemblyLoadContext *alc = mono_domain_ambient_alc (mono_domain_get ());
4620 return mono_assembly_loaded_internal (alc, aname, refonly);
4623 MonoAssembly *
4624 mono_assembly_loaded_internal (MonoAssemblyLoadContext *alc, MonoAssemblyName *aname, gboolean refonly)
4626 MonoAssembly *res;
4627 MonoAssemblyName maped_aname;
4629 aname = mono_assembly_remap_version (aname, &maped_aname);
4631 res = mono_assembly_invoke_search_hook_internal (alc, NULL, aname, refonly, FALSE);
4633 return res;
4637 * mono_assembly_loaded:
4638 * \param aname an assembly to look for.
4640 * This is used to determine if the specified assembly has been loaded
4642 * \returns NULL If the given \p aname assembly has not been loaded, or a pointer to
4643 * a \c MonoAssembly that matches the \c MonoAssemblyName specified.
4645 MonoAssembly*
4646 mono_assembly_loaded (MonoAssemblyName *aname)
4648 MonoAssembly *res;
4649 MONO_ENTER_GC_UNSAFE;
4650 res = mono_assembly_loaded_internal (mono_domain_ambient_alc (mono_domain_get ()), aname, FALSE);
4651 MONO_EXIT_GC_UNSAFE;
4652 return res;
4655 void
4656 mono_assembly_release_gc_roots (MonoAssembly *assembly)
4658 if (assembly == NULL || assembly == REFERENCE_MISSING)
4659 return;
4661 if (assembly_is_dynamic (assembly)) {
4662 int i;
4663 MonoDynamicImage *dynimg = (MonoDynamicImage *)assembly->image;
4664 for (i = 0; i < dynimg->image.module_count; ++i)
4665 mono_dynamic_image_release_gc_roots ((MonoDynamicImage *)dynimg->image.modules [i]);
4666 mono_dynamic_image_release_gc_roots (dynimg);
4671 * Returns whether mono_assembly_close_finish() must be called as
4672 * well. See comment for mono_image_close_except_pools() for why we
4673 * unload in two steps.
4675 gboolean
4676 mono_assembly_close_except_image_pools (MonoAssembly *assembly)
4678 GSList *tmp;
4679 g_return_val_if_fail (assembly != NULL, FALSE);
4681 if (assembly == REFERENCE_MISSING)
4682 return FALSE;
4684 /* Might be 0 already */
4685 if (mono_atomic_dec_i32 (&assembly->ref_count) > 0)
4686 return FALSE;
4688 MONO_PROFILER_RAISE (assembly_unloading, (assembly));
4690 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Unloading assembly %s [%p].", assembly->aname.name, assembly);
4692 mono_debug_close_image (assembly->image);
4694 mono_assemblies_lock ();
4695 loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
4696 mono_assemblies_unlock ();
4698 assembly->image->assembly = NULL;
4700 if (!mono_image_close_except_pools (assembly->image))
4701 assembly->image = NULL;
4703 for (tmp = assembly->friend_assembly_names; tmp; tmp = tmp->next) {
4704 MonoAssemblyName *fname = (MonoAssemblyName *)tmp->data;
4705 mono_assembly_name_free (fname);
4706 g_free (fname);
4708 g_slist_free (assembly->friend_assembly_names);
4709 g_free (assembly->basedir);
4711 MONO_PROFILER_RAISE (assembly_unloaded, (assembly));
4713 return TRUE;
4716 void
4717 mono_assembly_close_finish (MonoAssembly *assembly)
4719 g_assert (assembly && assembly != REFERENCE_MISSING);
4721 if (assembly->image)
4722 mono_image_close_finish (assembly->image);
4724 if (assembly_is_dynamic (assembly)) {
4725 g_free ((char*)assembly->aname.culture);
4726 } else {
4727 g_free (assembly);
4732 * mono_assembly_close:
4733 * \param assembly the assembly to release.
4735 * This method releases a reference to the \p assembly. The assembly is
4736 * only released when all the outstanding references to it are released.
4738 void
4739 mono_assembly_close (MonoAssembly *assembly)
4741 if (mono_assembly_close_except_image_pools (assembly))
4742 mono_assembly_close_finish (assembly);
4746 * mono_assembly_load_module:
4748 MonoImage*
4749 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
4751 ERROR_DECL (error);
4752 MonoImage *result = mono_assembly_load_module_checked (assembly, idx, error);
4753 mono_error_assert_ok (error);
4754 return result;
4757 MONO_API MonoImage*
4758 mono_assembly_load_module_checked (MonoAssembly *assembly, uint32_t idx, MonoError *error)
4760 return mono_image_load_file_for_image_checked (assembly->image, idx, error);
4765 * mono_assembly_foreach:
4766 * \param func function to invoke for each assembly loaded
4767 * \param user_data data passed to the callback
4769 * Invokes the provided \p func callback for each assembly loaded into
4770 * the runtime. The first parameter passed to the callback is the
4771 * \c MonoAssembly*, and the second parameter is the \p user_data.
4773 * This is done for all assemblies loaded in the runtime, not just
4774 * those loaded in the current application domain.
4776 void
4777 mono_assembly_foreach (GFunc func, gpointer user_data)
4779 GList *copy;
4782 * We make a copy of the list to avoid calling the callback inside the
4783 * lock, which could lead to deadlocks.
4785 mono_assemblies_lock ();
4786 copy = g_list_copy (loaded_assemblies);
4787 mono_assemblies_unlock ();
4789 g_list_foreach (loaded_assemblies, func, user_data);
4791 g_list_free (copy);
4795 * mono_assemblies_cleanup:
4797 * Free all resources used by this module.
4799 void
4800 mono_assemblies_cleanup (void)
4802 GSList *l;
4804 mono_os_mutex_destroy (&assemblies_mutex);
4805 mono_os_mutex_destroy (&assembly_binding_mutex);
4807 for (l = loaded_assembly_bindings; l; l = l->next) {
4808 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data;
4810 mono_assembly_binding_info_free (info);
4811 g_free (info);
4813 g_slist_free (loaded_assembly_bindings);
4815 free_assembly_asmctx_from_path_hooks ();
4816 free_assembly_load_hooks ();
4817 free_assembly_search_hooks ();
4818 free_assembly_preload_hooks ();
4821 /*LOCKING takes the assembly_binding lock*/
4822 void
4823 mono_assembly_cleanup_domain_bindings (guint32 domain_id)
4825 GSList **iter;
4827 mono_assembly_binding_lock ();
4828 iter = &loaded_assembly_bindings;
4829 while (*iter) {
4830 GSList *l = *iter;
4831 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data;
4833 if (info->domain_id == domain_id) {
4834 *iter = l->next;
4835 mono_assembly_binding_info_free (info);
4836 g_free (info);
4837 g_slist_free_1 (l);
4838 } else {
4839 iter = &l->next;
4842 mono_assembly_binding_unlock ();
4846 * Holds the assembly of the application, for
4847 * System.Diagnostics.Process::MainModule
4849 static MonoAssembly *main_assembly=NULL;
4852 * mono_assembly_set_main:
4854 void
4855 mono_assembly_set_main (MonoAssembly *assembly)
4857 main_assembly = assembly;
4861 * mono_assembly_get_main:
4863 * Returns: the assembly for the application, the first assembly that is loaded by the VM
4865 MonoAssembly *
4866 mono_assembly_get_main (void)
4868 return (main_assembly);
4872 * mono_assembly_get_image:
4873 * \param assembly The assembly to retrieve the image from
4875 * \returns the \c MonoImage associated with this assembly.
4877 MonoImage*
4878 mono_assembly_get_image (MonoAssembly *assembly)
4880 MonoImage *res;
4881 MONO_ENTER_GC_UNSAFE;
4882 res = mono_assembly_get_image_internal (assembly);
4883 MONO_EXIT_GC_UNSAFE;
4884 return res;
4887 MonoImage*
4888 mono_assembly_get_image_internal (MonoAssembly *assembly)
4890 MONO_REQ_GC_UNSAFE_MODE;
4891 return assembly->image;
4895 * mono_assembly_get_name:
4896 * \param assembly The assembly to retrieve the name from
4898 * The returned name's lifetime is the same as \p assembly's.
4900 * \returns the \c MonoAssemblyName associated with this assembly.
4902 MonoAssemblyName *
4903 mono_assembly_get_name (MonoAssembly *assembly)
4905 MonoAssemblyName *res;
4906 MONO_ENTER_GC_UNSAFE;
4907 res = mono_assembly_get_name_internal (assembly);
4908 MONO_EXIT_GC_UNSAFE;
4909 return res;
4912 MonoAssemblyName *
4913 mono_assembly_get_name_internal (MonoAssembly *assembly)
4915 MONO_REQ_GC_UNSAFE_MODE;
4916 return &assembly->aname;
4920 * mono_register_bundled_assemblies:
4922 void
4923 mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies)
4925 bundles = assemblies;
4928 #define MONO_DECLSEC_FORMAT_10 0x3C
4929 #define MONO_DECLSEC_FORMAT_20 0x2E
4930 #define MONO_DECLSEC_FIELD 0x53
4931 #define MONO_DECLSEC_PROPERTY 0x54
4933 #define SKIP_VISIBILITY_XML_ATTRIBUTE ("\"SkipVerification\"")
4934 #define SKIP_VISIBILITY_ATTRIBUTE_NAME ("System.Security.Permissions.SecurityPermissionAttribute")
4935 #define SKIP_VISIBILITY_ATTRIBUTE_SIZE (sizeof (SKIP_VISIBILITY_ATTRIBUTE_NAME) - 1)
4936 #define SKIP_VISIBILITY_PROPERTY_NAME ("SkipVerification")
4937 #define SKIP_VISIBILITY_PROPERTY_SIZE (sizeof (SKIP_VISIBILITY_PROPERTY_NAME) - 1)
4939 static gboolean
4940 mono_assembly_try_decode_skip_verification_param (const char *p, const char **resp, gboolean *abort_decoding)
4942 int len;
4943 switch (*p++) {
4944 case MONO_DECLSEC_PROPERTY:
4945 break;
4946 case MONO_DECLSEC_FIELD:
4947 default:
4948 *abort_decoding = TRUE;
4949 return FALSE;
4950 break;
4953 if (*p++ != MONO_TYPE_BOOLEAN) {
4954 *abort_decoding = TRUE;
4955 return FALSE;
4958 /* property name length */
4959 len = mono_metadata_decode_value (p, &p);
4961 if (len >= SKIP_VISIBILITY_PROPERTY_SIZE && !memcmp (p, SKIP_VISIBILITY_PROPERTY_NAME, SKIP_VISIBILITY_PROPERTY_SIZE)) {
4962 p += len;
4963 return *p;
4965 p += len + 1;
4967 *resp = p;
4968 return FALSE;
4971 static gboolean
4972 mono_assembly_try_decode_skip_verification (const char *p, const char *endn)
4974 int i, j, num, len, params_len;
4976 if (*p == MONO_DECLSEC_FORMAT_10) {
4977 gsize read, written;
4978 char *res = g_convert (p, endn - p, "UTF-8", "UTF-16LE", &read, &written, NULL);
4979 if (res) {
4980 gboolean found = strstr (res, SKIP_VISIBILITY_XML_ATTRIBUTE) != NULL;
4981 g_free (res);
4982 return found;
4984 return FALSE;
4986 if (*p++ != MONO_DECLSEC_FORMAT_20)
4987 return FALSE;
4989 /* number of encoded permission attributes */
4990 num = mono_metadata_decode_value (p, &p);
4991 for (i = 0; i < num; ++i) {
4992 gboolean is_valid = FALSE;
4993 gboolean abort_decoding = FALSE;
4995 /* attribute name length */
4996 len = mono_metadata_decode_value (p, &p);
4998 /* We don't really need to fully decode the type. Comparing the name is enough */
4999 is_valid = len >= SKIP_VISIBILITY_ATTRIBUTE_SIZE && !memcmp (p, SKIP_VISIBILITY_ATTRIBUTE_NAME, SKIP_VISIBILITY_ATTRIBUTE_SIZE);
5001 p += len;
5003 /*size of the params table*/
5004 params_len = mono_metadata_decode_value (p, &p);
5005 if (is_valid) {
5006 const char *params_end = p + params_len;
5008 /* number of parameters */
5009 len = mono_metadata_decode_value (p, &p);
5011 for (j = 0; j < len; ++j) {
5012 if (mono_assembly_try_decode_skip_verification_param (p, &p, &abort_decoding))
5013 return TRUE;
5014 if (abort_decoding)
5015 break;
5017 p = params_end;
5018 } else {
5019 p += params_len;
5023 return FALSE;
5027 gboolean
5028 mono_assembly_has_skip_verification (MonoAssembly *assembly)
5030 MonoTableInfo *t;
5031 guint32 cols [MONO_DECL_SECURITY_SIZE];
5032 const char *blob;
5033 int i, len;
5035 if (MONO_SECMAN_FLAG_INIT (assembly->skipverification))
5036 return MONO_SECMAN_FLAG_GET_VALUE (assembly->skipverification);
5038 t = &assembly->image->tables [MONO_TABLE_DECLSECURITY];
5040 for (i = 0; i < t->rows; ++i) {
5041 mono_metadata_decode_row (t, i, cols, MONO_DECL_SECURITY_SIZE);
5042 if ((cols [MONO_DECL_SECURITY_PARENT] & MONO_HAS_DECL_SECURITY_MASK) != MONO_HAS_DECL_SECURITY_ASSEMBLY)
5043 continue;
5044 if (cols [MONO_DECL_SECURITY_ACTION] != SECURITY_ACTION_REQMIN)
5045 continue;
5047 blob = mono_metadata_blob_heap (assembly->image, cols [MONO_DECL_SECURITY_PERMISSIONSET]);
5048 len = mono_metadata_decode_blob_size (blob, &blob);
5049 if (!len)
5050 continue;
5052 if (mono_assembly_try_decode_skip_verification (blob, blob + len)) {
5053 MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, TRUE);
5054 return TRUE;
5058 MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, FALSE);
5059 return FALSE;
5062 MonoAssemblyContextKind
5063 mono_asmctx_get_kind (const MonoAssemblyContext *ctx)
5065 return ctx->kind;
5068 static const char *
5069 mono_asmctx_get_name (const MonoAssemblyContext *asmctx)
5071 static const char* names [] = {
5072 "DEFAULT",
5073 "REFONLY",
5074 "LOADFROM",
5075 "INDIVIDIUAL",
5077 g_assert (asmctx->kind >= 0 && asmctx->kind <= MONO_ASMCTX_LAST);
5078 return names [asmctx->kind];