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