2003-10-31 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_arch_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                                         /* memcpy might clobber EDX so restore it */
205                                         x86_mov_reg_membase (p, X86_EDX, X86_EBP, ARGP_POS, 4);
206                                 }
207                         } else {
208                                 /* it's an enum value */
209                                 simpletype = sig->params [i - 1]->data.klass->enum_basetype->type;
210                                 goto enum_marshal;
211                         }
212                         break;
213                 case MONO_TYPE_I8:
214                 case MONO_TYPE_U8:
215                 case MONO_TYPE_R8:
216                         x86_push_membase (p, X86_EDX, arg_pos + 4);
217                         x86_push_membase (p, X86_EDX, arg_pos);
218                         break;
219                 default:
220                         g_error ("Can't trampoline 0x%x", sig->params [i - 1]->type);
221                 }
222         }
223
224         if (sig->hasthis) {
225                 if (sig->call_convention != MONO_CALL_THISCALL) {
226                         x86_mov_reg_membase (p, X86_EDX, X86_EBP, THIS_POS, 4);
227                         x86_push_reg (p, X86_EDX);
228                 } else {
229                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, THIS_POS, 4);
230                 }
231         }
232
233         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
234                 MonoClass *klass = sig->ret->data.klass;
235                 if (!klass->enumtype) {
236                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
237                         x86_push_membase (p, X86_ECX, 0);
238                 }
239         }
240
241         /* 
242          * Insert call to function 
243          */
244         x86_mov_reg_membase (p, X86_EDX, X86_EBP, FUNC_ADDR_POS, 4);
245         x86_call_reg (p, X86_EDX);
246
247         /*
248          * Handle retval.
249          * Small integer and pointer values are in EAX.
250          * Long integers are in EAX:EDX.
251          * FP values are on the FP stack.
252          */
253
254         if (sig->ret->byref || string_ctor) {
255                 x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
256                 x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
257         } else {
258                 simpletype = sig->ret->type;
259         enum_retvalue:
260                 switch (simpletype) {
261                 case MONO_TYPE_BOOLEAN:
262                 case MONO_TYPE_I1:
263                 case MONO_TYPE_U1:
264                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
265                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 1);
266                         break;
267                 case MONO_TYPE_CHAR:
268                 case MONO_TYPE_I2:
269                 case MONO_TYPE_U2:
270                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
271                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 2);
272                         break;
273                 case MONO_TYPE_I4:
274                 case MONO_TYPE_U4:
275                 case MONO_TYPE_I:
276                 case MONO_TYPE_U:
277                 case MONO_TYPE_CLASS:
278                 case MONO_TYPE_OBJECT:
279                 case MONO_TYPE_SZARRAY:
280                 case MONO_TYPE_ARRAY:
281                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
282                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
283                         break;
284                 case MONO_TYPE_STRING: 
285                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
286                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
287                         break;
288                 case MONO_TYPE_R4:
289                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
290                         x86_fst_membase (p, X86_ECX, 0, FALSE, TRUE);
291                         break;
292                 case MONO_TYPE_R8:
293                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
294                         x86_fst_membase (p, X86_ECX, 0, TRUE, TRUE);
295                         break;
296                 case MONO_TYPE_I8:
297                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
298                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
299                         x86_mov_membase_reg (p, X86_ECX, 4, X86_EDX, 4);
300                         break;
301                 case MONO_TYPE_VALUETYPE:
302                         if (sig->ret->data.klass->enumtype) {
303                                 simpletype = sig->ret->data.klass->enum_basetype->type;
304                                 goto enum_retvalue;
305                         }
306                 case MONO_TYPE_VOID:
307                         break;
308                 default:
309                         g_error ("Can't handle as return value 0x%x", sig->ret->type);
310                 }
311         }
312
313         /*
314          * Standard epilog.
315          */
316         x86_leave (p);
317         x86_ret (p);
318
319         g_assert (p - code_buffer < code_size);
320         res = (MonoPIFunc)g_memdup (code_buffer, p - code_buffer);
321
322         g_hash_table_insert (cache, sig, res);
323
324         return res;
325 }
326
327 #define MINV_POS  (- sizeof (MonoInvocation))
328 #define STACK_POS (MINV_POS - sizeof (stackval) * sig->param_count)
329 #define TYPE_OFFSET (G_STRUCT_OFFSET (stackval, type))
330
331 /*
332  * Returns a pointer to a native function that can be used to
333  * call the specified method.
334  * The function created will receive the arguments according
335  * to the call convention specified in the method.
336  * This function works by creating a MonoInvocation structure,
337  * filling the fields in and calling ves_exec_method on it.
338  * Still need to figure out how to handle the exception stuff
339  * across the managed/unmanaged boundary.
340  */
341 void *
342 mono_arch_create_method_pointer (MonoMethod *method)
343 {
344         MonoMethodSignature *sig;
345         MonoJitInfo *ji;
346         unsigned char *p, *code_buffer;
347         gint32 local_size;
348         gint32 stackval_pos, arg_pos = 8;
349         int i, size, align, cpos;
350         int *vtbuf;
351
352         sig = method->signature;
353
354         code_buffer = p = alloca (512); /* FIXME: check for overflows... */
355         vtbuf = alloca (sizeof(int)*sig->param_count);
356
357         local_size = sizeof (MonoInvocation) + sizeof (stackval) * (sig->param_count + 1);
358
359         local_size += 7;
360         local_size &= ~7;
361
362         stackval_pos = -local_size;
363
364         cpos = 0;
365         for (i = 0; i < sig->param_count; i++) {
366                 MonoType *type = sig->params [i];
367                 vtbuf [i] = -1;
368                 if (type->type == MONO_TYPE_VALUETYPE) {
369                         MonoClass *klass = type->data.klass;
370                         if (klass->enumtype)
371                                 continue;
372                         size = mono_class_native_size (klass, &align);
373                         cpos += align - 1;
374                         cpos &= ~(align - 1);
375                         vtbuf [i] = cpos;
376                         cpos += size;
377                 }       
378         }
379
380         cpos += 7;
381         cpos &= ~7;
382
383         local_size += cpos;
384
385         /*
386          * Standard function prolog.
387          */
388         x86_push_reg (p, X86_EBP);
389         x86_mov_reg_reg (p, X86_EBP, X86_ESP, 4);
390         x86_alu_reg_imm (p, X86_SUB, X86_ESP, local_size);
391
392         /*
393          * Initialize MonoInvocation fields, first the ones known now.
394          */
395         x86_mov_reg_imm (p, X86_EAX, 0);
396         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex)), X86_EAX, 4);
397         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex_handler)), X86_EAX, 4);
398         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, child)), X86_EAX, 4);
399         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, parent)), X86_EAX, 4);
400         /*
401          * Set the method pointer.
402          */
403         x86_mov_membase_imm (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, method)), (int)method, 4);
404
405         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref && !sig->ret->data.klass->enumtype) 
406                 arg_pos += 4;
407
408         /*
409          * Handle this.
410          */
411         if (sig->hasthis) {
412                 if (sig->call_convention != MONO_CALL_THISCALL) {
413                         /*
414                          * Grab it from the stack, otherwise it's already in ECX.
415                          */
416                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, arg_pos, 4);
417                         arg_pos += 4;
418                 }
419                 x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, obj)), X86_ECX, 4);
420         }
421         /*
422          * Handle the arguments. stackval_pos is the posset of the stackval array from EBP.
423          * arg_pos is the offset from EBP to the incoming arg on the stack.
424          * We just call stackval_from_data to handle all the (nasty) issues....
425          */
426         x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
427         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, stack_args)), X86_EAX, 4);
428         for (i = 0; i < sig->param_count; ++i) {
429                 if (vtbuf [i] >= 0) {
430                         x86_lea_membase (p, X86_EAX, X86_EBP, - local_size + vtbuf [i]);
431                         x86_mov_membase_reg (p, X86_EBP, stackval_pos, X86_EAX, 4);
432                 }
433                 x86_mov_reg_imm (p, X86_ECX, stackval_from_data);
434                 x86_lea_membase (p, X86_EDX, X86_EBP, arg_pos);
435                 x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
436                 x86_push_imm (p, sig->pinvoke);
437                 x86_push_reg (p, X86_EDX);
438                 x86_push_reg (p, X86_EAX);
439                 x86_push_imm (p, sig->params [i]);
440                 x86_call_reg (p, X86_ECX);
441                 x86_alu_reg_imm (p, X86_SUB, X86_ESP, 16);
442                 stackval_pos += sizeof (stackval);
443                 /* fixme: alignment */
444                 if (sig->pinvoke)
445                         arg_pos += mono_type_native_stack_size (sig->params [i], &align);
446                 else
447                         arg_pos += mono_type_stack_size (sig->params [i], &align);
448         }
449
450         /*
451          * Handle the return value storage area.
452          */
453         x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
454         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, retval)), X86_EAX, 4);
455         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
456                 MonoClass *klass  = sig->ret->data.klass;
457                 if (!klass->enumtype) {
458                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, 8, 4);
459                         x86_mov_membase_reg (p, X86_EBP, stackval_pos, X86_ECX, 4);
460                 }
461         }
462
463         /*
464          * Call the method.
465          */
466         x86_lea_membase (p, X86_EAX, X86_EBP, MINV_POS);
467         x86_push_reg (p, X86_EAX);
468         x86_mov_reg_imm (p, X86_EDX, ves_exec_method);
469         x86_call_reg (p, X86_EDX);
470         
471         /*
472          * Move the return value to the proper place.
473          */
474         x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
475         if (sig->ret->byref) {
476                 x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
477         } else {
478                 int simpletype = sig->ret->type;        
479         enum_retvalue:
480                 switch (sig->ret->type) {
481                 case MONO_TYPE_VOID:
482                         break;
483                 case MONO_TYPE_BOOLEAN:
484                 case MONO_TYPE_I1:
485                 case MONO_TYPE_U1:
486                         x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 1);
487                         break;
488                 case MONO_TYPE_CHAR:
489                 case MONO_TYPE_I2:
490                 case MONO_TYPE_U2:
491                         x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 2);
492                         break;
493                 case MONO_TYPE_I4:
494                 case MONO_TYPE_U4:
495                 case MONO_TYPE_I:
496                 case MONO_TYPE_U:
497                 case MONO_TYPE_OBJECT:
498                 case MONO_TYPE_STRING:
499                 case MONO_TYPE_CLASS:
500                         x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
501                         break;
502                 case MONO_TYPE_I8:
503                         x86_mov_reg_membase (p, X86_EDX, X86_EAX, 4, 4);
504                         x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
505                         break;
506                 case MONO_TYPE_R8:
507                         x86_fld_membase (p, X86_EAX, 0, TRUE);
508                         break;
509                 case MONO_TYPE_VALUETYPE:
510                         if (sig->ret->data.klass->enumtype) {
511                                 simpletype = sig->ret->data.klass->enum_basetype->type;
512                                 goto enum_retvalue;
513                         }
514                 
515                         x86_push_imm (p, sig->pinvoke);
516                         x86_push_membase (p, X86_EBP, stackval_pos);
517                         x86_push_reg (p, X86_EAX);
518                         x86_push_imm (p, sig->ret);
519                         x86_mov_reg_imm (p, X86_ECX, stackval_to_data);
520                         x86_call_reg (p, X86_ECX);
521                         x86_alu_reg_imm (p, X86_SUB, X86_ESP, 16);
522                         
523                         break;
524                 default:
525                         g_error ("Type 0x%x not handled yet in thunk creation", sig->ret->type);
526                         break;
527                 }
528         }
529         
530         /*
531          * Standard epilog.
532          */
533         x86_leave (p);
534         x86_ret (p);
535
536         g_assert (p - code_buffer < 512);
537
538         ji = g_new0 (MonoJitInfo, 1);
539         ji->method = method;
540         ji->code_size = p - code_buffer;
541         ji->code_start = g_memdup (code_buffer, p - code_buffer);
542
543         mono_jit_info_table_add (mono_root_domain, ji);
544
545         return ji->code_start;
546 }