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