pinvoke bug fix
[mono.git] / mono / metadata / loader.c
1 /*
2  * loader.c: Image Loader 
3  *
4  * Authors:
5  *   Paolo Molaro (lupus@ximian.com)
6  *   Miguel de Icaza (miguel@ximian.com)
7  *
8  * (C) 2001 Ximian, Inc.
9  *
10  * This file is used by the interpreter and the JIT engine to locate
11  * assemblies.  Used to load AssemblyRef and later to resolve various
12  * kinds of `Refs'.
13  *
14  * TODO:
15  *   This should keep track of the assembly versions that we are loading.
16  *
17  */
18 #include <config.h>
19 #include <glib.h>
20 #include <gmodule.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <mono/metadata/metadata.h>
24 #include <mono/metadata/image.h>
25 #include <mono/metadata/assembly.h>
26 #include <mono/metadata/tokentype.h>
27 #include <mono/metadata/cil-coff.h>
28 #include <mono/metadata/tabledefs.h>
29 #include "cli.h"
30
31
32 static char *dll_map[] = {
33         "libc", "libc.so.6",
34         "libm", "libm.so.6",
35         "cygwin1.dll", "libc.so.6", 
36         NULL, NULL
37 };
38
39 static const char *
40 mono_map_dll (const char *name)
41 {
42         int i = 0;
43
44         while (dll_map [i]) {
45                 if (!strcmp (dll_map [i], name))
46                         return  dll_map [i + 1];
47                 i += 2;
48         }
49
50         return name;
51 }
52
53 guint32
54 mono_typedef_from_name (MonoImage *image, const char *name, 
55                         const char *nspace, guint32 *mlist)
56 {
57         MonoMetadata *m = &image->metadata;
58         MonoTableInfo *t = &m->tables [MONO_TABLE_TYPEDEF];
59         guint32 i;
60         guint32 cols [MONO_TYPEDEF_SIZE];
61
62         for (i=0; i < t->rows; ++i) {
63                 mono_metadata_decode_row (t, i, cols, MONO_TYPEDEF_SIZE);
64                 if (strcmp (name, mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAME])) == 0 
65                                 && strcmp (nspace, mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAMESPACE])) == 0) {
66                         if (mlist)
67                                 *mlist = cols [MONO_TYPEDEF_METHOD_LIST];
68                         return i + 1;
69                 }
70         }
71         g_assert_not_reached ();
72         return 0;
73 }
74
75 /**
76  * mono_get_string_class_info:
77  * @ttoken: pointer to location to store type definition token
78  * @cl: pointer where image will be stored
79  *
80  * This routine locates information about the System.String class. A reference
81  * to the image containing the class is returned in @cl. The type definition 
82  * token is returned in @ttoken. 
83  *
84  * Returns: the method definition token for System.String::.ctor (char *)
85  */
86
87 guint32
88 mono_get_string_class_info (guint *ttoken, MonoImage **cl)
89 {
90         static guint32 ctor = 0, tt = 0;
91         enum MonoImageOpenStatus status = MONO_IMAGE_OK; 
92         MonoAssembly *ass;
93         static MonoImage *corlib;
94         MonoMetadata *m;
95         MonoTableInfo *t;
96         guint32 cols [MAX (MONO_TYPEDEF_SIZE, MONO_METHOD_SIZE)];
97         guint32 ncols [MONO_TYPEDEF_SIZE];
98         guint32 i, first = 0, last = 0;
99         const char *name, *nspace;
100
101         if (ctor) {
102                 *ttoken = tt;
103                 *cl = corlib;
104                 return ctor;
105         }
106
107         ass = mono_assembly_open (CORLIB_NAME, NULL, &status);
108         g_assert (status == MONO_IMAGE_OK);
109         g_assert (ass != NULL);
110         
111         *cl = corlib = ass->image;
112         g_assert (corlib != NULL);
113        
114         m = &corlib->metadata;
115         t = &m->tables [MONO_TABLE_TYPEDEF];
116
117         for (i = 0; i < t->rows; i++) {
118                 mono_metadata_decode_row (t, i, cols, MONO_TYPEDEF_SIZE);
119                 name = mono_metadata_string_heap (m, cols[1]);
120                 nspace = mono_metadata_string_heap (m, cols[2]);
121
122                 if (((cols [0] & TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK) == TYPE_ATTRIBUTE_CLASS) &&
123                     !strcmp (nspace, "System") && !strcmp (name, "String")) {
124
125                         *ttoken = tt = MONO_TOKEN_TYPE_DEF | (i + 1);
126
127                         first = cols [5] - 1;
128
129                         if (i + 1 < t->rows) {
130                                 mono_metadata_decode_row (t, i + 1, ncols, 
131                                                           MONO_TYPEDEF_SIZE);
132                                 last =  ncols [5] - 1;
133                         } else
134                                 last = m->tables [MONO_TABLE_METHOD].rows;
135                         break;
136                 }
137         }
138
139         g_assert (last - first > 0);
140
141         t = &m->tables [MONO_TABLE_METHOD];
142         g_assert (last < t->rows);
143
144         for (i = first; i < last; i++) {
145                 const char *ptr;
146                 int len;
147                 guint8 sig[] = { 0x20, 0x01, 0x01, 0x0f, 0x03 };
148                 mono_metadata_decode_row (t, i, cols, MONO_METHOD_SIZE);
149
150                 if (!strcmp (mono_metadata_string_heap (m, cols [3]), 
151                              ".ctor") &&
152                     (cols [2] & METHOD_ATTRIBUTE_SPECIAL_NAME)) {
153
154                         ptr = mono_metadata_blob_heap (m, cols [4]);
155                         len = mono_metadata_decode_value (ptr, &ptr);
156
157                         if (len == 5 && !memcmp (ptr, sig, len)) {
158                                 ctor = MONO_TOKEN_METHOD_DEF | (i + 1);
159                                 break;
160                         }
161                 } 
162
163         }
164
165         return ctor;
166 }
167
168 static void
169 methoddef_from_memberref (MonoImage *image, guint32 index, MonoImage **rimage, guint32 *rindex)
170 {
171         MonoMetadata *m = &image->metadata;
172         MonoTableInfo *tables = m->tables;
173         guint32 cols[6];
174         guint32 nindex, sig_len, msig_len, class, i;
175         const char *sig, *msig, *mname, *name, *nspace;
176
177         mono_metadata_decode_row (&tables [MONO_TABLE_MEMBERREF], index-1, cols, 3);
178         nindex = cols [MONO_MEMBERREF_CLASS] >> MEMBERREF_PARENT_BITS;
179         class = cols [MONO_MEMBERREF_CLASS] & MEMBERREF_PARENT_MASK;
180         /*g_print ("methodref: 0x%x 0x%x %s\n", class, nindex,
181                 mono_metadata_string_heap (m, cols [MONO_MEMBERREF_NAME]));*/
182         sig = mono_metadata_blob_heap (m, cols [MONO_MEMBERREF_SIGNATURE]);
183         sig_len = mono_metadata_decode_blob_size (sig, &sig);
184         mname = mono_metadata_string_heap (m, cols [MONO_MEMBERREF_NAME]);
185
186         switch (class) {
187         case MEMBERREF_PARENT_TYPEREF: {
188                 guint32 scopeindex, scopetable;
189
190                 mono_metadata_decode_row (&tables [MONO_TABLE_TYPEREF], nindex-1, cols, MONO_TYPEREF_SIZE);
191                 scopeindex = cols [MONO_TYPEREF_SCOPE] >> RESOLTION_SCOPE_BITS;
192                 scopetable = cols [MONO_TYPEREF_SCOPE] & RESOLTION_SCOPE_MASK;
193                 /*g_print ("typeref: 0x%x 0x%x %s.%s\n", scopetable, scopeindex,
194                         mono_metadata_string_heap (m, cols [MONO_TYPEREF_NAMESPACE]),
195                         mono_metadata_string_heap (m, cols [MONO_TYPEREF_NAME]));*/
196                 switch (scopetable) {
197                 case RESOLTION_SCOPE_ASSEMBLYREF:
198                         /*
199                          * To find the method we have the following info:
200                          * *) name and namespace of the class from the TYPEREF table
201                          * *) name and signature of the method from the MEMBERREF table
202                          */
203                         nspace = mono_metadata_string_heap (m, cols [MONO_TYPEREF_NAMESPACE]);
204                         name = mono_metadata_string_heap (m, cols [MONO_TYPEREF_NAME]);
205
206                         /* this will triggered by references to mscorlib */
207                         g_assert (image->references [scopeindex-1] != NULL);
208
209                         image = image->references [scopeindex-1]->image;
210
211                         m = &image->metadata;
212                         tables = &m->tables [MONO_TABLE_METHOD];
213                         mono_typedef_from_name (image, name, nspace, &i);
214                         /* mostly dumb search for now */
215                         for (;i < tables->rows; ++i) {
216                                 mono_metadata_decode_row (tables, i, cols, MONO_METHOD_SIZE);
217                                 msig = mono_metadata_blob_heap (m, cols [MONO_METHOD_SIGNATURE]);
218                                 msig_len = mono_metadata_decode_blob_size (msig, &msig);
219                                 
220                                 if (strcmp (mname, mono_metadata_string_heap (m, cols [MONO_METHOD_NAME])) == 0 
221                                                 && sig_len == msig_len
222                                                 && strncmp (sig, msig, sig_len) == 0) {
223                                         *rimage = image;
224                                         *rindex = i + 1;
225                                         return;
226                                 }
227                         }
228                         g_assert_not_reached ();
229                         break;
230                 default:
231                         g_assert_not_reached ();
232                 }
233                 break;
234         }
235         default:
236                 g_assert_not_reached ();
237         }
238 }
239
240 static ffi_type *
241 ves_map_ffi_type (MonoType *type)
242 {
243         ffi_type *rettype;
244
245         if (!type)
246                 return &ffi_type_void;
247
248         switch (type->type) {
249         case MONO_TYPE_I1:
250                 rettype = &ffi_type_sint8;
251                 break;
252         case MONO_TYPE_BOOLEAN:
253         case MONO_TYPE_U1:
254                 rettype = &ffi_type_uint8;
255                 break;
256         case MONO_TYPE_I2:
257                 rettype = &ffi_type_sint16;
258                 break;
259         case MONO_TYPE_U2:
260         case MONO_TYPE_CHAR:
261                 rettype = &ffi_type_uint16;
262                 break;
263         case MONO_TYPE_I4:
264                 rettype = &ffi_type_sint32;
265                 break;
266         case MONO_TYPE_U4:
267                 rettype = &ffi_type_sint32;
268                 break;
269         case MONO_TYPE_R4:
270                 rettype = &ffi_type_float;
271                 break;
272         case MONO_TYPE_R8:
273                 rettype = &ffi_type_double;
274                 break;
275         case MONO_TYPE_STRING:
276                 rettype = &ffi_type_pointer;
277                 break;
278         default:
279                 g_warning ("not implemented");
280                 g_assert_not_reached ();
281         }
282
283         return rettype;
284 }
285
286 static void
287 fill_pinvoke_info (MonoImage *image, MonoMethodPInvoke *piinfo, int index)
288 {
289         MonoMethod *mh = &piinfo->method;
290         MonoTableInfo *tables = image->metadata.tables;
291         MonoTableInfo *im = &tables [MONO_TABLE_IMPLMAP];
292         MonoTableInfo *mr = &tables [MONO_TABLE_MODULEREF];
293         guint32 im_cols [4];
294         guint32 mr_cols [1];
295         const char *import = NULL;
296         const char *scope = NULL;
297         char *full_name;
298         GModule *gmodule;
299         ffi_type **args, *rettype;
300         int i, acount;
301
302         for (i = 0; i < im->rows; i++) {
303                         
304                 mono_metadata_decode_row (im, i, im_cols, 4);
305
306                 if ((im_cols[1] >> 1) == index + 1) {
307
308                         import = mono_metadata_string_heap (&image->metadata, 
309                                                             im_cols [2]);
310
311                         mono_metadata_decode_row (mr, im_cols [3] - 1, mr_cols,
312                                                   1);
313                         
314                         scope = mono_metadata_string_heap (&image->metadata, 
315                                                            mr_cols [0]);
316                 }
317         }
318
319         g_assert (import && scope);
320
321         scope = mono_map_dll (scope);
322         full_name = g_module_build_path (NULL, scope);
323         gmodule = g_module_open (full_name, G_MODULE_BIND_LAZY);
324         g_free (full_name);
325
326         g_assert (gmodule);
327
328         piinfo->cif = g_new (ffi_cif , 1);
329         piinfo->piflags = im_cols [0];
330
331         g_module_symbol (gmodule, import, &mh->addr); 
332
333         g_assert (mh->addr);
334
335         acount = mh->signature->param_count;
336
337         args = g_new (ffi_type *, acount);
338
339         for (i = 0; i < acount; i++)
340                 args[i] = ves_map_ffi_type (mh->signature->params [i]->type);
341
342         rettype = ves_map_ffi_type (mh->signature->ret->type);
343         
344         if (!ffi_prep_cif (piinfo->cif, FFI_DEFAULT_ABI, acount, rettype, 
345                            args) == FFI_OK) {
346                 g_warning ("prepare pinvoke failed");
347                 g_assert_not_reached ();
348         }
349 }
350
351 MonoMethod *
352 mono_get_method (MonoImage *image, guint32 token)
353 {
354         MonoMethod *result;
355         MonoMetadata *m = &image->metadata;
356         int table = mono_metadata_token_table (token);
357         int index = mono_metadata_token_index (token);
358         MonoTableInfo *tables = m->tables;
359         const char *loc, *name;
360         const char *sig = NULL;
361         int size;
362         guint32 cols[6];
363
364         if (table == MONO_TABLE_METHOD && (result = g_hash_table_lookup (image->method_cache, GINT_TO_POINTER (token))))
365                         return result;
366         
367         if (table != MONO_TABLE_METHOD) {
368                 g_assert (table == MONO_TABLE_MEMBERREF);
369                 methoddef_from_memberref (image, index, &image, &token);
370                 return mono_get_method (image, MONO_TOKEN_METHOD_DEF | token);
371         }
372
373         mono_metadata_decode_row (&tables [table], index - 1, cols, 6);
374
375         if (cols [1] & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) {
376                 MonoAssembly *corlib;
377
378                 corlib = mono_assembly_open (CORLIB_NAME, NULL, NULL);
379
380                 name = mono_metadata_string_heap (m, cols[3]);
381
382                 /* all internal calls must be inside corlib */
383                 g_assert (corlib->image == image);
384
385                 result = (MonoMethod *)g_new0 (MonoMethod, 1);
386
387                 result->addr = mono_lookup_internal_call (name);
388
389                 g_assert (result->addr != NULL);
390
391         } else if (cols [2] & METHOD_ATTRIBUTE_PINVOKE_IMPL) {
392
393                 result = (MonoMethod *)g_new0 (MonoMethodPInvoke, 1);
394         } else {
395
396                 result = (MonoMethod *)g_new0 (MonoMethodNormal, 1);
397         }
398
399         result->image = image;
400         result->flags = cols [2];
401         result->iflags = cols [1];
402         result->name = mono_metadata_string_heap (m, cols [3]);
403
404         if (!sig) /* already taken from the methodref */
405                 sig = mono_metadata_blob_heap (m, cols [4]);
406         size = mono_metadata_decode_blob_size (sig, &sig);
407         result->signature = mono_metadata_parse_method_signature (m, 0, sig, 
408                                                                   NULL);
409
410
411         if (result->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) {
412                 fill_pinvoke_info (image, (MonoMethodPInvoke *)result, 
413                                    index - 1);
414         } else if (!(result->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) {
415                 /* if this is a methodref from another module/assembly, this fails */
416                 loc = mono_cli_rva_map ((MonoCLIImageInfo *)image->image_info, cols [0]);
417                 g_assert (loc);
418                 ((MonoMethodNormal *)result)->header = 
419                         mono_metadata_parse_mh (m, loc);
420         }
421
422         g_hash_table_insert (image->method_cache, GINT_TO_POINTER (token), result);
423
424         return result;
425 }
426
427 void
428 mono_free_method  (MonoMethod *method)
429 {
430         mono_metadata_free_method_signature (method->signature);
431         if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) {
432                 MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *)method;
433                 g_free (piinfo->cif->arg_types);
434                 g_free (piinfo->cif);
435         } else if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) {
436                 mono_metadata_free_mh (((MonoMethodNormal *)method)->header);
437         }
438
439         g_free (method);
440 }
441