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