2003-08-21 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / arch / x86 / tramp.c
1 /*
2  * Create trampolines to invoke arbitrary functions.
3  * 
4  * Copyright (C) Ximian Inc.
5  * 
6  * Authors: 
7  *   Paolo Molaro (lupus@ximian.com)
8  *   Dietmar Maurer (dietmar@ximian.com)
9  * 
10  */
11
12 #include "config.h"
13 #include <stdlib.h>
14 #include <string.h>
15 #include "x86-codegen.h"
16 #include "mono/metadata/class.h"
17 #include "mono/metadata/tabledefs.h"
18 #include "mono/interpreter/interp.h"
19 #include "mono/metadata/appdomain.h"
20 #include "mono/metadata/marshal.h"
21
22 /*
23  * The resulting function takes the form:
24  * void func (void (*callme)(), void *retval, void *this_obj, stackval *arguments);
25  */
26 #define FUNC_ADDR_POS   8
27 #define RETVAL_POS      12
28 #define THIS_POS        16
29 #define ARGP_POS        20
30 #define LOC_POS -4
31
32 #define ARG_SIZE        sizeof (stackval)
33
34 MonoPIFunc
35 mono_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor)
36 {
37         unsigned char *p, *code_buffer;
38         guint32 stack_size = 0, code_size = 50;
39         guint32 arg_pos, simpletype;
40         int i, stringp;
41         static GHashTable *cache = NULL;
42         MonoPIFunc res;
43
44         if (!cache) 
45                 cache = g_hash_table_new ((GHashFunc)mono_signature_hash, 
46                                           (GCompareFunc)mono_metadata_signature_equal);
47
48         if ((res = (MonoPIFunc)g_hash_table_lookup (cache, sig)))
49                 return res;
50
51         if (sig->hasthis) {
52                 stack_size += sizeof (gpointer);
53                 code_size += 10;
54         }
55         
56         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref && !sig->ret->data.klass->enumtype) {
57                 stack_size += sizeof (gpointer);
58                 code_size += 5;
59         }
60
61         for (i = 0; i < sig->param_count; ++i) {
62                 if (sig->params [i]->byref) {
63                         stack_size += sizeof (gpointer);
64                         code_size += 20;
65                         continue;
66                 }
67                 simpletype = sig->params [i]->type;
68 enum_calc_size:
69                 switch (simpletype) {
70                 case MONO_TYPE_BOOLEAN:
71                 case MONO_TYPE_CHAR:
72                 case MONO_TYPE_I1:
73                 case MONO_TYPE_U1:
74                 case MONO_TYPE_I2:
75                 case MONO_TYPE_U2:
76                 case MONO_TYPE_I4:
77                 case MONO_TYPE_U4:
78                 case MONO_TYPE_I:
79                 case MONO_TYPE_U:
80                 case MONO_TYPE_PTR:
81                 case MONO_TYPE_SZARRAY:
82                 case MONO_TYPE_CLASS:
83                 case MONO_TYPE_OBJECT:
84                 case MONO_TYPE_STRING:
85                         stack_size += 4;
86                         code_size += i < 10 ? 5 : 8;
87                         break;
88                 case MONO_TYPE_VALUETYPE: {
89                         int size;
90                         if (sig->params [i]->data.klass->enumtype) {
91                                 simpletype = sig->params [i]->data.klass->enum_basetype->type;
92                                 goto enum_calc_size;
93                         }
94                         if ((size = mono_class_native_size (sig->params [i]->data.klass, NULL)) != 4) {
95                                 stack_size += size + 3;
96                                 stack_size &= ~3;
97                                 code_size += 32;
98                         } else {
99                                 stack_size += 4;
100                                 code_size += i < 10 ? 5 : 8;
101                         }
102                         break;
103                 }
104                 case MONO_TYPE_I8:
105                         stack_size += 8;
106                         code_size += i < 10 ? 5 : 8;
107                         break;
108                 case MONO_TYPE_R4:
109                         stack_size += 4;
110                         code_size += i < 10 ? 10 : 13;
111                         break;
112                 case MONO_TYPE_R8:
113                         stack_size += 8;
114                         code_size += i < 10 ? 7 : 10;
115                         break;
116                 default:
117                         g_error ("Can't trampoline 0x%x", sig->params [i]->type);
118                 }
119         }
120         /*
121          * FIXME: take into account large return values.
122          */
123
124         code_buffer = p = alloca (code_size);
125
126         /*
127          * Standard function prolog.
128          */
129         x86_push_reg (p, X86_EBP);
130         x86_mov_reg_reg (p, X86_EBP, X86_ESP, 4);
131         /*
132          * and align to 16 byte boundary...
133          */
134         stack_size += 15;
135         stack_size &= ~15;
136
137         if (stack_size)
138                 x86_alu_reg_imm (p, X86_SUB, X86_ESP, stack_size);
139
140         /*
141          * EDX has the pointer to the args.
142          */
143         x86_mov_reg_membase (p, X86_EDX, X86_EBP, ARGP_POS, 4);
144
145         /*
146          * Push arguments in reverse order.
147          */
148         stringp = 0;
149         for (i = sig->param_count; i; --i) {
150                 arg_pos = ARG_SIZE * (i - 1);
151                 if (sig->params [i - 1]->byref) {
152                         x86_push_membase (p, X86_EDX, arg_pos);
153                         continue;
154                 }
155                 simpletype = sig->params [i - 1]->type;
156 enum_marshal:
157                 switch (simpletype) {
158                 case MONO_TYPE_BOOLEAN:
159                 case MONO_TYPE_I1:
160                 case MONO_TYPE_U1:
161                 case MONO_TYPE_I2:
162                 case MONO_TYPE_U2:
163                 case MONO_TYPE_CHAR:
164                 case MONO_TYPE_I4:
165                 case MONO_TYPE_U4:
166                 case MONO_TYPE_I:
167                 case MONO_TYPE_U:
168                 case MONO_TYPE_PTR:
169                 case MONO_TYPE_OBJECT:
170                 case MONO_TYPE_STRING:
171                         x86_push_membase (p, X86_EDX, arg_pos);
172                         break;
173                 case MONO_TYPE_R4:
174                         x86_alu_reg_imm (p, X86_SUB, X86_ESP, 4);
175                         x86_fld_membase (p, X86_EDX, arg_pos, TRUE);
176                         x86_fst_membase (p, X86_ESP, 0, FALSE, TRUE);
177                         break;
178                 case MONO_TYPE_CLASS:
179                         x86_push_membase (p, X86_EDX, arg_pos);
180                         break;
181                 case MONO_TYPE_SZARRAY:
182                         x86_push_membase (p, X86_EDX, arg_pos);
183                         break;
184                 case MONO_TYPE_VALUETYPE:
185                         if (!sig->params [i - 1]->data.klass->enumtype) {
186                                 int size = mono_class_native_size (sig->params [i - 1]->data.klass, NULL);
187                                 if (size == 4) {
188                                         /* it's a structure that fits in 4 bytes, need to push the value pointed to */
189                                         x86_mov_reg_membase (p, X86_EAX, X86_EDX, arg_pos, 4);
190                                         x86_push_regp (p, X86_EAX);
191                                 } else {
192                                         int ss = size;
193                                         ss += 3;
194                                         ss &= ~3;
195
196                                         x86_alu_reg_imm (p, X86_SUB, X86_ESP, ss);
197                                         x86_push_imm (p, size);
198                                         x86_push_membase (p, X86_EDX, arg_pos);
199                                         x86_lea_membase (p, X86_EAX, X86_ESP, 2*4);
200                                         x86_push_reg (p, X86_EAX);
201                                         x86_mov_reg_imm (p, X86_EAX, memcpy);
202                                         x86_call_reg (p, X86_EAX);
203                                         x86_alu_reg_imm (p, X86_ADD, X86_ESP, 12);
204                                 }
205                         } else {
206                                 /* it's an enum value */
207                                 simpletype = sig->params [i - 1]->data.klass->enum_basetype->type;
208                                 goto enum_marshal;
209                         }
210                         break;
211                 case MONO_TYPE_I8:
212                 case MONO_TYPE_U8:
213                 case MONO_TYPE_R8:
214                         x86_push_membase (p, X86_EDX, arg_pos + 4);
215                         x86_push_membase (p, X86_EDX, arg_pos);
216                         break;
217                 default:
218                         g_error ("Can't trampoline 0x%x", sig->params [i - 1]->type);
219                 }
220         }
221
222         if (sig->hasthis) {
223                 if (sig->call_convention != MONO_CALL_THISCALL) {
224                         x86_mov_reg_membase (p, X86_EDX, X86_EBP, THIS_POS, 4);
225                         x86_push_reg (p, X86_EDX);
226                 } else {
227                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, THIS_POS, 4);
228                 }
229         }
230
231         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
232                 MonoClass *klass = sig->ret->data.klass;
233                 if (!klass->enumtype) {
234                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
235                         x86_push_membase (p, X86_ECX, 0);
236                 }
237         }
238
239         /* 
240          * Insert call to function 
241          */
242         x86_mov_reg_membase (p, X86_EDX, X86_EBP, FUNC_ADDR_POS, 4);
243         x86_call_reg (p, X86_EDX);
244
245         /*
246          * Handle retval.
247          * Small integer and pointer values are in EAX.
248          * Long integers are in EAX:EDX.
249          * FP values are on the FP stack.
250          */
251
252         if (sig->ret->byref || string_ctor) {
253                 x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
254                 x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
255         } else {
256                 simpletype = sig->ret->type;
257         enum_retvalue:
258                 switch (simpletype) {
259                 case MONO_TYPE_BOOLEAN:
260                 case MONO_TYPE_I1:
261                 case MONO_TYPE_U1:
262                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
263                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 1);
264                         break;
265                 case MONO_TYPE_CHAR:
266                 case MONO_TYPE_I2:
267                 case MONO_TYPE_U2:
268                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
269                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 2);
270                         break;
271                 case MONO_TYPE_I4:
272                 case MONO_TYPE_U4:
273                 case MONO_TYPE_I:
274                 case MONO_TYPE_U:
275                 case MONO_TYPE_CLASS:
276                 case MONO_TYPE_OBJECT:
277                 case MONO_TYPE_SZARRAY:
278                 case MONO_TYPE_ARRAY:
279                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
280                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
281                         break;
282                 case MONO_TYPE_STRING: 
283                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
284                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
285                         break;
286                 case MONO_TYPE_R4:
287                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
288                         x86_fst_membase (p, X86_ECX, 0, FALSE, TRUE);
289                         break;
290                 case MONO_TYPE_R8:
291                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
292                         x86_fst_membase (p, X86_ECX, 0, TRUE, TRUE);
293                         break;
294                 case MONO_TYPE_I8:
295                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
296                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
297                         x86_mov_membase_reg (p, X86_ECX, 4, X86_EDX, 4);
298                         break;
299                 case MONO_TYPE_VALUETYPE:
300                         if (sig->ret->data.klass->enumtype) {
301                                 simpletype = sig->ret->data.klass->enum_basetype->type;
302                                 goto enum_retvalue;
303                         }
304                 case MONO_TYPE_VOID:
305                         break;
306                 default:
307                         g_error ("Can't handle as return value 0x%x", sig->ret->type);
308                 }
309         }
310
311         /*
312          * Standard epilog.
313          */
314         x86_leave (p);
315         x86_ret (p);
316
317         g_assert (p - code_buffer < code_size);
318         res = (MonoPIFunc)g_memdup (code_buffer, p - code_buffer);
319
320         g_hash_table_insert (cache, sig, res);
321
322         return res;
323 }
324
325 #define MINV_POS  (- sizeof (MonoInvocation))
326 #define STACK_POS (MINV_POS - sizeof (stackval) * sig->param_count)
327 #define TYPE_OFFSET (G_STRUCT_OFFSET (stackval, type))
328
329 /*
330  * Returns a pointer to a native function that can be used to
331  * call the specified method.
332  * The function created will receive the arguments according
333  * to the call convention specified in the method.
334  * This function works by creating a MonoInvocation structure,
335  * filling the fields in and calling ves_exec_method on it.
336  * Still need to figure out how to handle the exception stuff
337  * across the managed/unmanaged boundary.
338  */
339 void *
340 mono_create_method_pointer (MonoMethod *method)
341 {
342         MonoMethodSignature *sig;
343         MonoJitInfo *ji;
344         unsigned char *p, *code_buffer;
345         gint32 local_size;
346         gint32 stackval_pos, arg_pos = 8;
347         int i, size, align, cpos;
348         int *vtbuf;
349
350         /*
351          * If it is a static P/Invoke method, we can just return the pointer
352          * to the method implementation.
353          */
354         if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL && method->addr) {
355                 ji = g_new0 (MonoJitInfo, 1);
356                 ji->method = method;
357                 ji->code_size = 1;
358                 ji->code_start = method->addr;
359
360                 mono_jit_info_table_add (mono_root_domain, ji);
361                 return method->addr;
362         }
363
364         sig = method->signature;
365
366         code_buffer = p = alloca (512); /* FIXME: check for overflows... */
367         vtbuf = alloca (sizeof(int)*sig->param_count);
368
369         local_size = sizeof (MonoInvocation) + sizeof (stackval) * (sig->param_count + 1);
370
371         local_size += 7;
372         local_size &= ~7;
373
374         stackval_pos = -local_size;
375
376         cpos = 0;
377         for (i = 0; i < sig->param_count; i++) {
378                 MonoType *type = sig->params [i];
379                 vtbuf [i] = -1;
380                 if (type->type == MONO_TYPE_VALUETYPE) {
381                         MonoClass *klass = type->data.klass;
382                         if (klass->enumtype)
383                                 continue;
384                         size = mono_class_native_size (klass, &align);
385                         cpos += align - 1;
386                         cpos &= ~(align - 1);
387                         vtbuf [i] = cpos;
388                         cpos += size;
389                 }       
390         }
391
392         cpos += 7;
393         cpos &= ~7;
394
395         local_size += cpos;
396
397         /*
398          * Standard function prolog.
399          */
400         x86_push_reg (p, X86_EBP);
401         x86_mov_reg_reg (p, X86_EBP, X86_ESP, 4);
402         x86_alu_reg_imm (p, X86_SUB, X86_ESP, local_size);
403
404         /*
405          * Initialize MonoInvocation fields, first the ones known now.
406          */
407         x86_mov_reg_imm (p, X86_EAX, 0);
408         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex)), X86_EAX, 4);
409         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex_handler)), X86_EAX, 4);
410         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, child)), X86_EAX, 4);
411         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, parent)), X86_EAX, 4);
412         /*
413          * Set the method pointer.
414          */
415         x86_mov_membase_imm (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, method)), (int)method, 4);
416
417         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref && !sig->ret->data.klass->enumtype) 
418                 arg_pos += 4;
419
420         /*
421          * Handle this.
422          */
423         if (sig->hasthis) {
424                 if (sig->call_convention != MONO_CALL_THISCALL) {
425                         /*
426                          * Grab it from the stack, otherwise it's already in ECX.
427                          */
428                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, arg_pos, 4);
429                         arg_pos += 4;
430                 }
431                 x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, obj)), X86_ECX, 4);
432         }
433         /*
434          * Handle the arguments. stackval_pos is the posset of the stackval array from EBP.
435          * arg_pos is the offset from EBP to the incoming arg on the stack.
436          * We just call stackval_from_data to handle all the (nasty) issues....
437          */
438         x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
439         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, stack_args)), X86_EAX, 4);
440         for (i = 0; i < sig->param_count; ++i) {
441                 if (vtbuf [i] >= 0) {
442                         x86_lea_membase (p, X86_EAX, X86_EBP, - local_size + vtbuf [i]);
443                         x86_mov_membase_reg (p, X86_EBP, stackval_pos, X86_EAX, 4);
444                 }
445                 x86_mov_reg_imm (p, X86_ECX, stackval_from_data);
446                 x86_lea_membase (p, X86_EDX, X86_EBP, arg_pos);
447                 x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
448                 x86_push_imm (p, sig->pinvoke);
449                 x86_push_reg (p, X86_EDX);
450                 x86_push_reg (p, X86_EAX);
451                 x86_push_imm (p, sig->params [i]);
452                 x86_call_reg (p, X86_ECX);
453                 x86_alu_reg_imm (p, X86_SUB, X86_ESP, 16);
454                 stackval_pos += sizeof (stackval);
455                 /* fixme: alignment */
456                 if (sig->pinvoke)
457                         arg_pos += mono_type_native_stack_size (sig->params [i], &align);
458                 else
459                         arg_pos += mono_type_stack_size (sig->params [i], &align);
460         }
461
462         /*
463          * Handle the return value storage area.
464          */
465         x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
466         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, retval)), X86_EAX, 4);
467         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
468                 MonoClass *klass  = sig->ret->data.klass;
469                 if (!klass->enumtype) {
470                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, 8, 4);
471                         x86_mov_membase_reg (p, X86_EBP, stackval_pos, X86_ECX, 4);
472                 }
473         }
474
475         /*
476          * Call the method.
477          */
478         x86_lea_membase (p, X86_EAX, X86_EBP, MINV_POS);
479         x86_push_reg (p, X86_EAX);
480         x86_mov_reg_imm (p, X86_EDX, ves_exec_method);
481         x86_call_reg (p, X86_EDX);
482         
483         /*
484          * Move the return value to the proper place.
485          */
486         x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
487         if (sig->ret->byref) {
488                 x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
489         } else {
490                 int simpletype = sig->ret->type;        
491         enum_retvalue:
492                 switch (sig->ret->type) {
493                 case MONO_TYPE_VOID:
494                         break;
495                 case MONO_TYPE_BOOLEAN:
496                         x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 1);
497                         break;
498                 case MONO_TYPE_I4:
499                 case MONO_TYPE_U4:
500                 case MONO_TYPE_I:
501                 case MONO_TYPE_U:
502                 case MONO_TYPE_OBJECT:
503                 case MONO_TYPE_STRING:
504                 case MONO_TYPE_CLASS:
505                         x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
506                         break;
507                 case MONO_TYPE_I8:
508                         x86_mov_reg_membase (p, X86_EDX, X86_EAX, 4, 4);
509                         x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
510                         break;
511                 case MONO_TYPE_R8:
512                         x86_fld_membase (p, X86_EAX, 0, TRUE);
513                         break;
514                 case MONO_TYPE_VALUETYPE:
515                         if (sig->ret->data.klass->enumtype) {
516                                 simpletype = sig->ret->data.klass->enum_basetype->type;
517                                 goto enum_retvalue;
518                         }
519                 
520                         x86_push_imm (p, sig->pinvoke);
521                         x86_push_membase (p, X86_EBP, stackval_pos);
522                         x86_push_reg (p, X86_EAX);
523                         x86_push_imm (p, sig->ret);
524                         x86_mov_reg_imm (p, X86_ECX, stackval_to_data);
525                         x86_call_reg (p, X86_ECX);
526                         x86_alu_reg_imm (p, X86_SUB, X86_ESP, 16);
527                         
528                         break;
529                 default:
530                         g_error ("Type 0x%x not handled yet in thunk creation", sig->ret->type);
531                         break;
532                 }
533         }
534         
535         /*
536          * Standard epilog.
537          */
538         x86_leave (p);
539         x86_ret (p);
540
541         g_assert (p - code_buffer < 512);
542
543         ji = g_new0 (MonoJitInfo, 1);
544         ji->method = method;
545         ji->code_size = p - code_buffer;
546         ji->code_start = g_memdup (code_buffer, p - code_buffer);
547
548         mono_jit_info_table_add (mono_root_domain, ji);
549
550         return ji->code_start;
551 }