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