Sun Sep 23 13:44:57 CEST 2001 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 "x86-codegen.h"
12 #include "mono/metadata/class.h"
13 #include "mono/interpreter/interp.h"
14
15 /*
16  * The resulting function takes the form:
17  * void func (void (*callme)(), void *retval, void *this_obj, stackval *arguments);
18  */
19 #define FUNC_ADDR_POS   8
20 #define RETVAL_POS      12
21 #define THIS_POS        16
22 #define ARGP_POS        20
23 #define LOC_POS -4
24
25 #define ARG_SIZE        sizeof (stackval)
26
27 static char *
28 mono_get_ansi_string (MonoObject *o)
29 {
30         MonoStringObject *s = (MonoStringObject *)o;
31         char *as, *vector;
32         int i;
33
34         g_assert (o != NULL);
35
36         if (!s->length)
37                 return g_strdup ("");
38
39         vector = s->c_str->vector;
40
41         g_assert (vector != NULL);
42
43         as = g_malloc (s->length + 1);
44
45         /* fixme: replace with a real unicode/ansi conversion */
46         for (i = 0; i < s->length; i++) {
47                 as [i] = vector [i*2];
48         }
49
50         as [i] = '\0';
51
52         return as;
53 }
54
55 MonoPIFunc
56 mono_create_trampoline (MonoMethod *method)
57 {
58         MonoMethodSignature *sig;
59         unsigned char *p, *code_buffer;
60         guint32 local_size = 0, stack_size = 0, code_size = 30;
61         guint32 arg_pos;
62         int i, stringp;
63
64         sig = method->signature;
65         
66         if (sig->hasthis) {
67                 stack_size += sizeof (gpointer);
68                 code_size += 5;
69         }
70         
71         for (i = 0; i < sig->param_count; ++i) {
72                 if (sig->params [i]->byref) {
73                         stack_size += sizeof (gpointer);
74                         code_size += i < 10 ? 5 : 8;
75                         continue;
76                 }
77                 switch (sig->params [i]->type) {
78                 case MONO_TYPE_BOOLEAN:
79                 case MONO_TYPE_CHAR:
80                 case MONO_TYPE_I1:
81                 case MONO_TYPE_U1:
82                 case MONO_TYPE_I2:
83                 case MONO_TYPE_U2:
84                 case MONO_TYPE_I4:
85                 case MONO_TYPE_U4:
86                 case MONO_TYPE_I:
87                 case MONO_TYPE_U:
88                 case MONO_TYPE_PTR:
89                 case MONO_TYPE_R4:
90                 case MONO_TYPE_SZARRAY:
91                 case MONO_TYPE_CLASS:
92                 case MONO_TYPE_OBJECT:
93                         stack_size += 4;
94                         code_size += i < 10 ? 5 : 8;
95                         break;
96                 case MONO_TYPE_STRING:
97                         stack_size += 4;
98                         code_size += 20;
99                         local_size++;
100                         break;
101                 case MONO_TYPE_I8:
102                         stack_size += 8;
103                         code_size += i < 10 ? 5 : 8;
104                         break;
105                 case MONO_TYPE_R8:
106                         stack_size += 8;
107                         code_size += i < 10 ? 7 : 10;
108                         break;
109                 default:
110                         g_error ("Can't trampoline 0x%x", sig->params [i]->type);
111                 }
112         }
113         /*
114          * FIXME: take into account large return values.
115          */
116
117         code_buffer = p = alloca (code_size);
118
119         /*
120          * Standard function prolog.
121          */
122         x86_push_reg (p, X86_EBP);
123         x86_mov_reg_reg (p, X86_EBP, X86_ESP, 4);
124         /*
125          * We store some local vars here to handle string pointers.
126          * and align to 16 byte boundary...
127          */
128         if (local_size) {
129                 x86_alu_reg_imm (p, X86_SUB, X86_ESP, local_size * 4);
130                 stack_size = (stack_size * local_size * 4) % 16;
131         } else {
132                 stack_size = stack_size % 16;
133         }
134         x86_alu_reg_imm (p, X86_SUB, X86_ESP, stack_size);
135
136         /*
137          * EDX has the pointer to the args.
138          */
139         x86_mov_reg_membase (p, X86_EDX, X86_EBP, ARGP_POS, 4);
140
141         /*
142          * Push arguments in reverse order.
143          */
144         stringp = 0;
145         for (i = sig->param_count; i; --i) {
146                 arg_pos = ARG_SIZE * (i - 1);
147                 if (sig->params [i - 1]->byref) {
148                         x86_push_membase (p, X86_EDX, arg_pos);
149                         continue;
150                 }
151                 switch (sig->params [i - 1]->type) {
152                 case MONO_TYPE_I1:
153                 case MONO_TYPE_U1:
154                 case MONO_TYPE_I2:
155                 case MONO_TYPE_U2:
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_R8:
168                         x86_alu_reg_imm (p, X86_SUB, X86_ESP, 8);
169                         x86_fld_membase (p, X86_EDX, arg_pos, TRUE);
170                         x86_fst_membase (p, X86_ESP, 0, TRUE, TRUE);
171                         break;
172                 case MONO_TYPE_STRING:
173                         /*if (frame->method->flags & PINVOKE_ATTRIBUTE_CHAR_SET_ANSI*/
174                         x86_push_membase (p, X86_EDX, arg_pos);
175                         x86_mov_reg_imm (p, X86_EDX, mono_get_ansi_string);
176                         x86_call_reg (p, X86_EDX);
177                         x86_alu_reg_imm (p, X86_SUB, X86_ESP, 4);
178                         x86_push_reg (p, X86_EAX);
179                         /*
180                          * Store the pointer in a local we'll free later.
181                          */
182                         stringp++;
183                         x86_mov_membase_reg (p, X86_EBP, LOC_POS * stringp, X86_EAX, 4);
184                         /*
185                          * we didn't save the reg: restore it here.
186                          */
187                         x86_mov_reg_membase (p, X86_EDX, X86_EBP, ARGP_POS, 4);
188                         break;
189                 case MONO_TYPE_I8:
190                         x86_push_membase (p, X86_EDX, arg_pos + 4);
191                         x86_push_membase (p, X86_EDX, arg_pos);
192                         break;
193                 case MONO_TYPE_BOOLEAN:
194                 case MONO_TYPE_CHAR:
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_I4:
227                 case MONO_TYPE_U4:
228                 case MONO_TYPE_I:
229                 case MONO_TYPE_U:
230                 case MONO_TYPE_CLASS:
231                 case MONO_TYPE_OBJECT:
232                 case MONO_TYPE_STRING: /* this is going to cause large pains... */
233                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
234                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
235                         break;
236                 case MONO_TYPE_R4:
237                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
238                         x86_fst_membase (p, X86_ECX, 0, FALSE, TRUE);
239                         break;
240                 case MONO_TYPE_R8:
241                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
242                         x86_fst_membase (p, X86_ECX, 0, TRUE, TRUE);
243                         break;
244                 case MONO_TYPE_I8:
245                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
246                         x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
247                         x86_mov_membase_reg (p, X86_ECX, 4, X86_EDX, 4);
248                         break;
249                 case MONO_TYPE_VOID:
250                         break;
251                 default:
252                         g_error ("Can't handle as return value 0x%x", sig->ret->type);
253                 }
254         }
255
256         /*
257          * free the allocated strings.
258          */
259         if (local_size)
260                 x86_mov_reg_imm (p, X86_EDX, g_free);
261         for (i = 1; i <= local_size; ++i) {
262                 x86_push_membase (p, X86_EBP, LOC_POS * i);
263                 x86_call_reg (p, X86_EDX);
264         }
265         /*
266          * Standard epilog.
267          */
268         x86_leave (p);
269         x86_ret (p);
270
271         return g_memdup (code_buffer, p - code_buffer);
272 }
273
274 #define MINV_POS  (- sizeof (MonoInvocation))
275 #define STACK_POS (MINV_POS - sizeof (stackval) * sig->param_count)
276 #define OBJ_POS   8
277 #define TYPE_OFFSET (G_STRUCT_OFFSET (stackval, type))
278
279 /*
280  * Returns a pointer to a native function that can be used to
281  * call the specified method.
282  * The function created will receive the arguments according
283  * to the call convention specified in the method.
284  * This function works by creating a MonoInvocation structure,
285  * filling the fields in and calling ves_exec_method on it.
286  * Still need to figure out how to handle the exception stuff
287  * across the managed/unmanaged boundary.
288  */
289 void *
290 mono_create_method_pointer (MonoMethod *method)
291 {
292         MonoMethodSignature *sig;
293         unsigned char *p, *code_buffer;
294         gint32 local_size;
295         gint32 stackval_pos, arg_pos = 8;
296         int i;
297
298         /*
299          * If it is a static P/Invoke method, we can just return the pointer
300          * to the method implementation.
301          */
302         sig = method->signature;
303
304         code_buffer = p = alloca (512); /* FIXME: check for overflows... */
305
306         local_size = sizeof (MonoInvocation) + sizeof (stackval) * (sig->param_count + 1);
307         stackval_pos = -local_size;
308
309         /*
310          * Standard function prolog with magic trick.
311          */
312         x86_jump_code (p, code_buffer + 8);
313         *p++ = 'M';
314         *p++ = 'o';
315         *(void**)p = method;
316         p += 4;
317         x86_push_reg (p, X86_EBP);
318         x86_mov_reg_reg (p, X86_EBP, X86_ESP, 4);
319         x86_alu_reg_imm (p, X86_SUB, X86_ESP, local_size);
320
321         /*
322          * Initialize MonoInvocation fields, first the ones known now.
323          */
324         x86_mov_reg_imm (p, X86_EAX, 0);
325         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex)), X86_EAX, 4);
326         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex_handler)), X86_EAX, 4);
327         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, child)), X86_EAX, 4);
328         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, parent)), X86_EAX, 4);
329         /*
330          * Set the method pointer.
331          */
332         x86_mov_membase_imm (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, method)), (int)method, 4);
333
334         /*
335          * Handle this.
336          */
337         if (sig->hasthis) {
338                 if (sig->call_convention != MONO_CALL_THISCALL) {
339                         /*
340                          * Grab it from the stack, otherwise it's already in ECX.
341                          */
342                         x86_mov_reg_membase (p, X86_ECX, X86_EBP, OBJ_POS, 4);
343                         arg_pos += 4;
344                 }
345                 x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, obj)), X86_ECX, 4);
346         }
347         /*
348          * Handle the arguments. stackval_pos is the posset of the stackval array from EBP.
349          * arg_pos is the offset from EBP to the incoming arg on the stack.
350          * We just call stackval_from_data to handle all the (nasty) issues....
351          */
352         for (i = 0; i < sig->param_count; ++i) {
353                 x86_mov_reg_imm (p, X86_ECX, stackval_from_data);
354                 x86_lea_membase (p, X86_EDX, X86_EBP, arg_pos);
355                 x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
356                 x86_push_reg (p, X86_EDX);
357                 x86_push_reg (p, X86_EAX);
358                 x86_push_imm (p, sig->params [i]);
359                 x86_call_reg (p, X86_ECX);
360                 x86_alu_reg_imm (p, X86_SUB, X86_ESP, 12);
361                 stackval_pos += sizeof (stackval);
362                 arg_pos += 4;
363                 if (!sig->params [i]->byref) {
364                         switch (sig->params [i]->type) {
365                         case MONO_TYPE_I8:
366                         case MONO_TYPE_R8:
367                                 arg_pos += 4;
368                                 break;
369                         case MONO_TYPE_VALUETYPE:
370                                 g_assert_not_reached (); /* Not implemented yet. */
371                         default:
372                                 break;
373                         }
374                 }
375         }
376
377         /*
378          * Handle the return value storage area.
379          */
380         x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
381         x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, retval)), X86_EAX, 4);
382
383         /*
384          * Call the method.
385          */
386         x86_lea_membase (p, X86_EAX, X86_EBP, MINV_POS);
387         x86_push_reg (p, X86_EAX);
388         x86_mov_reg_imm (p, X86_EDX, ves_exec_method);
389         x86_call_reg (p, X86_EDX);
390
391         /*
392          * Move the return value to the proper place.
393          */
394         x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
395         if (sig->ret->byref) {
396                 x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
397         } else {
398                 switch (sig->ret->type) {
399                 case MONO_TYPE_VOID:
400                         break;
401                 case MONO_TYPE_I4:
402                 case MONO_TYPE_U4:
403                 case MONO_TYPE_I:
404                 case MONO_TYPE_U:
405                 case MONO_TYPE_OBJECT:
406                 case MONO_TYPE_STRING:
407                 case MONO_TYPE_CLASS:
408                         x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
409                         break;
410                 case MONO_TYPE_I8:
411                         x86_mov_reg_membase (p, X86_EDX, X86_EAX, 4, 4);
412                         x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
413                         break;
414                 case MONO_TYPE_R8:
415                         x86_fld_membase (p, X86_EAX, 0, TRUE);
416                         break;
417                 default:
418                         g_error ("Type 0x%x not handled yet in thunk creation", sig->ret->type);
419                         break;
420                 }
421         }
422         
423         /*
424          * Standard epilog.
425          */
426         x86_leave (p);
427         x86_ret (p);
428
429         return g_memdup (code_buffer, p - code_buffer);
430 }
431
432