2007-01-04 Miguel de Icaza <miguel@novell.com>
[mono-project.git] / mono / arch / x86 / tramp.c
blobfab5a55325fa8ff4d140b51a8959f44f1ab714ad
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 *
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"
23 * The resulting function takes the form:
24 * void func (void (*callme)(), void *retval, void *this_obj, stackval *arguments);
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
32 #define ARG_SIZE sizeof (stackval)
34 MonoPIFunc
35 mono_arch_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor)
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;
44 if (!cache)
45 cache = g_hash_table_new ((GHashFunc)mono_signature_hash,
46 (GCompareFunc)mono_metadata_signature_equal);
48 if ((res = (MonoPIFunc)g_hash_table_lookup (cache, sig)))
49 return res;
51 if (sig->hasthis) {
52 stack_size += sizeof (gpointer);
53 code_size += 10;
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;
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;
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_SZARRAY:
82 case MONO_TYPE_CLASS:
83 case MONO_TYPE_OBJECT:
84 case MONO_TYPE_STRING:
85 stack_size += 4;
86 code_size += i < 10 ? 5 : 8;
87 break;
88 case MONO_TYPE_VALUETYPE: {
89 int size;
90 if (sig->params [i]->data.klass->enumtype) {
91 simpletype = sig->params [i]->data.klass->enum_basetype->type;
92 goto enum_calc_size;
94 if ((size = mono_class_native_size (sig->params [i]->data.klass, NULL)) != 4) {
95 stack_size += size + 3;
96 stack_size &= ~3;
97 code_size += 32;
98 } else {
99 stack_size += 4;
100 code_size += i < 10 ? 5 : 8;
102 break;
104 case MONO_TYPE_I8:
105 stack_size += 8;
106 code_size += i < 10 ? 5 : 8;
107 break;
108 case MONO_TYPE_R4:
109 stack_size += 4;
110 code_size += i < 10 ? 10 : 13;
111 break;
112 case MONO_TYPE_R8:
113 stack_size += 8;
114 code_size += i < 10 ? 7 : 10;
115 break;
116 default:
117 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
121 * FIXME: take into account large return values.
124 code_buffer = p = alloca (code_size);
127 * Standard function prolog.
129 x86_push_reg (p, X86_EBP);
130 x86_mov_reg_reg (p, X86_EBP, X86_ESP, 4);
132 * and align to 16 byte boundary...
134 stack_size += 15;
135 stack_size &= ~15;
137 if (stack_size)
138 x86_alu_reg_imm (p, X86_SUB, X86_ESP, stack_size);
141 * EDX has the pointer to the args.
143 x86_mov_reg_membase (p, X86_EDX, X86_EBP, ARGP_POS, 4);
146 * Push arguments in reverse order.
148 stringp = 0;
149 for (i = sig->param_count; i; --i) {
150 arg_pos = ARG_SIZE * (i - 1);
151 if (sig->params [i - 1]->byref) {
152 x86_push_membase (p, X86_EDX, arg_pos);
153 continue;
155 simpletype = sig->params [i - 1]->type;
156 enum_marshal:
157 switch (simpletype) {
158 case MONO_TYPE_BOOLEAN:
159 case MONO_TYPE_I1:
160 case MONO_TYPE_U1:
161 case MONO_TYPE_I2:
162 case MONO_TYPE_U2:
163 case MONO_TYPE_CHAR:
164 case MONO_TYPE_I4:
165 case MONO_TYPE_U4:
166 case MONO_TYPE_I:
167 case MONO_TYPE_U:
168 case MONO_TYPE_PTR:
169 case MONO_TYPE_OBJECT:
170 case MONO_TYPE_STRING:
171 x86_push_membase (p, X86_EDX, arg_pos);
172 break;
173 case MONO_TYPE_R4:
174 x86_alu_reg_imm (p, X86_SUB, X86_ESP, 4);
175 x86_fld_membase (p, X86_EDX, arg_pos, TRUE);
176 x86_fst_membase (p, X86_ESP, 0, FALSE, TRUE);
177 break;
178 case MONO_TYPE_CLASS:
179 x86_push_membase (p, X86_EDX, arg_pos);
180 break;
181 case MONO_TYPE_SZARRAY:
182 x86_push_membase (p, X86_EDX, arg_pos);
183 break;
184 case MONO_TYPE_VALUETYPE:
185 if (!sig->params [i - 1]->data.klass->enumtype) {
186 int size = mono_class_native_size (sig->params [i - 1]->data.klass, NULL);
187 if (size == 4) {
188 /* it's a structure that fits in 4 bytes, need to push the value pointed to */
189 x86_mov_reg_membase (p, X86_EAX, X86_EDX, arg_pos, 4);
190 x86_push_regp (p, X86_EAX);
191 } else {
192 int ss = size;
193 ss += 3;
194 ss &= ~3;
196 x86_alu_reg_imm (p, X86_SUB, X86_ESP, ss);
197 x86_push_imm (p, size);
198 x86_push_membase (p, X86_EDX, arg_pos);
199 x86_lea_membase (p, X86_EAX, X86_ESP, 2*4);
200 x86_push_reg (p, X86_EAX);
201 x86_mov_reg_imm (p, X86_EAX, memcpy);
202 x86_call_reg (p, X86_EAX);
203 x86_alu_reg_imm (p, X86_ADD, X86_ESP, 12);
204 /* memcpy might clobber EDX so restore it */
205 x86_mov_reg_membase (p, X86_EDX, X86_EBP, ARGP_POS, 4);
207 } else {
208 /* it's an enum value */
209 simpletype = sig->params [i - 1]->data.klass->enum_basetype->type;
210 goto enum_marshal;
212 break;
213 case MONO_TYPE_I8:
214 case MONO_TYPE_U8:
215 case MONO_TYPE_R8:
216 x86_push_membase (p, X86_EDX, arg_pos + 4);
217 x86_push_membase (p, X86_EDX, arg_pos);
218 break;
219 default:
220 g_error ("Can't trampoline 0x%x", sig->params [i - 1]->type);
224 if (sig->hasthis) {
225 if (sig->call_convention != MONO_CALL_THISCALL) {
226 x86_mov_reg_membase (p, X86_EDX, X86_EBP, THIS_POS, 4);
227 x86_push_reg (p, X86_EDX);
228 } else {
229 x86_mov_reg_membase (p, X86_ECX, X86_EBP, THIS_POS, 4);
233 if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
234 MonoClass *klass = sig->ret->data.klass;
235 if (!klass->enumtype) {
236 x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
237 x86_push_membase (p, X86_ECX, 0);
242 * Insert call to function
244 x86_mov_reg_membase (p, X86_EDX, X86_EBP, FUNC_ADDR_POS, 4);
245 x86_call_reg (p, X86_EDX);
248 * Handle retval.
249 * Small integer and pointer values are in EAX.
250 * Long integers are in EAX:EDX.
251 * FP values are on the FP stack.
254 if (sig->ret->byref || string_ctor) {
255 x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
256 x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
257 } else {
258 simpletype = sig->ret->type;
259 enum_retvalue:
260 switch (simpletype) {
261 case MONO_TYPE_BOOLEAN:
262 case MONO_TYPE_I1:
263 case MONO_TYPE_U1:
264 x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
265 x86_mov_regp_reg (p, X86_ECX, X86_EAX, 1);
266 break;
267 case MONO_TYPE_CHAR:
268 case MONO_TYPE_I2:
269 case MONO_TYPE_U2:
270 x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
271 x86_mov_regp_reg (p, X86_ECX, X86_EAX, 2);
272 break;
273 case MONO_TYPE_I4:
274 case MONO_TYPE_U4:
275 case MONO_TYPE_I:
276 case MONO_TYPE_U:
277 case MONO_TYPE_CLASS:
278 case MONO_TYPE_OBJECT:
279 case MONO_TYPE_SZARRAY:
280 case MONO_TYPE_ARRAY:
281 x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
282 x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
283 break;
284 case MONO_TYPE_STRING:
285 x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
286 x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
287 break;
288 case MONO_TYPE_R4:
289 x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
290 x86_fst_membase (p, X86_ECX, 0, FALSE, TRUE);
291 break;
292 case MONO_TYPE_R8:
293 x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
294 x86_fst_membase (p, X86_ECX, 0, TRUE, TRUE);
295 break;
296 case MONO_TYPE_I8:
297 x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
298 x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
299 x86_mov_membase_reg (p, X86_ECX, 4, X86_EDX, 4);
300 break;
301 case MONO_TYPE_VALUETYPE:
302 if (sig->ret->data.klass->enumtype) {
303 simpletype = sig->ret->data.klass->enum_basetype->type;
304 goto enum_retvalue;
306 case MONO_TYPE_VOID:
307 break;
308 default:
309 g_error ("Can't handle as return value 0x%x", sig->ret->type);
314 * Standard epilog.
316 x86_leave (p);
317 x86_ret (p);
319 g_assert (p - code_buffer < code_size);
320 res = (MonoPIFunc)g_memdup (code_buffer, p - code_buffer);
322 g_hash_table_insert (cache, sig, res);
324 return res;
327 #define MINV_POS (- sizeof (MonoInvocation))
328 #define STACK_POS (MINV_POS - sizeof (stackval) * sig->param_count)
329 #define TYPE_OFFSET (G_STRUCT_OFFSET (stackval, type))
332 * Returns a pointer to a native function that can be used to
333 * call the specified method.
334 * The function created will receive the arguments according
335 * to the call convention specified in the method.
336 * This function works by creating a MonoInvocation structure,
337 * filling the fields in and calling ves_exec_method on it.
338 * Still need to figure out how to handle the exception stuff
339 * across the managed/unmanaged boundary.
341 void *
342 mono_arch_create_method_pointer (MonoMethod *method)
344 MonoMethodSignature *sig;
345 MonoJitInfo *ji;
346 unsigned char *p, *code_buffer;
347 gint32 local_size;
348 gint32 stackval_pos, arg_pos = 8;
349 int i, size, align, cpos;
350 int *vtbuf;
352 sig = method->signature;
354 code_buffer = p = alloca (512); /* FIXME: check for overflows... */
355 vtbuf = alloca (sizeof(int)*sig->param_count);
357 local_size = sizeof (MonoInvocation) + sizeof (stackval) * (sig->param_count + 1);
359 local_size += 7;
360 local_size &= ~7;
362 stackval_pos = -local_size;
364 cpos = 0;
365 for (i = 0; i < sig->param_count; i++) {
366 MonoType *type = sig->params [i];
367 vtbuf [i] = -1;
368 if (type->type == MONO_TYPE_VALUETYPE) {
369 MonoClass *klass = type->data.klass;
370 if (klass->enumtype)
371 continue;
372 size = mono_class_native_size (klass, &align);
373 cpos += align - 1;
374 cpos &= ~(align - 1);
375 vtbuf [i] = cpos;
376 cpos += size;
380 cpos += 7;
381 cpos &= ~7;
383 local_size += cpos;
386 * Standard function prolog.
388 x86_push_reg (p, X86_EBP);
389 x86_mov_reg_reg (p, X86_EBP, X86_ESP, 4);
390 x86_alu_reg_imm (p, X86_SUB, X86_ESP, local_size);
393 * Initialize MonoInvocation fields, first the ones known now.
395 x86_mov_reg_imm (p, X86_EAX, 0);
396 x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex)), X86_EAX, 4);
397 x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex_handler)), X86_EAX, 4);
398 x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, parent)), X86_EAX, 4);
400 * Set the method pointer.
402 x86_mov_membase_imm (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, method)), (int)method, 4);
404 if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref && !sig->ret->data.klass->enumtype)
405 arg_pos += 4;
408 * Handle this.
410 if (sig->hasthis) {
411 if (sig->call_convention != MONO_CALL_THISCALL) {
413 * Grab it from the stack, otherwise it's already in ECX.
415 x86_mov_reg_membase (p, X86_ECX, X86_EBP, arg_pos, 4);
416 arg_pos += 4;
418 x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, obj)), X86_ECX, 4);
421 * Handle the arguments. stackval_pos is the posset of the stackval array from EBP.
422 * arg_pos is the offset from EBP to the incoming arg on the stack.
423 * We just call stackval_from_data to handle all the (nasty) issues....
425 x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
426 x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, stack_args)), X86_EAX, 4);
427 for (i = 0; i < sig->param_count; ++i) {
428 if (vtbuf [i] >= 0) {
429 x86_lea_membase (p, X86_EAX, X86_EBP, - local_size + vtbuf [i]);
430 x86_mov_membase_reg (p, X86_EBP, stackval_pos, X86_EAX, 4);
432 x86_mov_reg_imm (p, X86_ECX, stackval_from_data);
433 x86_lea_membase (p, X86_EDX, X86_EBP, arg_pos);
434 x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
435 x86_push_imm (p, sig->pinvoke);
436 x86_push_reg (p, X86_EDX);
437 x86_push_reg (p, X86_EAX);
438 x86_push_imm (p, sig->params [i]);
439 x86_call_reg (p, X86_ECX);
440 x86_alu_reg_imm (p, X86_SUB, X86_ESP, 16);
441 stackval_pos += sizeof (stackval);
442 /* fixme: alignment */
443 if (sig->pinvoke)
444 arg_pos += mono_type_native_stack_size (sig->params [i], &align);
445 else
446 arg_pos += mono_type_stack_size (sig->params [i], &align);
450 * Handle the return value storage area.
452 x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
453 x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, retval)), X86_EAX, 4);
454 if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
455 MonoClass *klass = sig->ret->data.klass;
456 if (!klass->enumtype) {
457 x86_mov_reg_membase (p, X86_ECX, X86_EBP, 8, 4);
458 x86_mov_membase_reg (p, X86_EBP, stackval_pos, X86_ECX, 4);
463 * Call the method.
465 x86_lea_membase (p, X86_EAX, X86_EBP, MINV_POS);
466 x86_push_reg (p, X86_EAX);
467 x86_mov_reg_imm (p, X86_EDX, ves_exec_method);
468 x86_call_reg (p, X86_EDX);
471 * Move the return value to the proper place.
473 x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
474 if (sig->ret->byref) {
475 x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
476 } else {
477 int simpletype = sig->ret->type;
478 enum_retvalue:
479 switch (sig->ret->type) {
480 case MONO_TYPE_VOID:
481 break;
482 case MONO_TYPE_BOOLEAN:
483 case MONO_TYPE_I1:
484 case MONO_TYPE_U1:
485 x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 1);
486 break;
487 case MONO_TYPE_CHAR:
488 case MONO_TYPE_I2:
489 case MONO_TYPE_U2:
490 x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 2);
491 break;
492 case MONO_TYPE_I4:
493 case MONO_TYPE_U4:
494 case MONO_TYPE_I:
495 case MONO_TYPE_U:
496 case MONO_TYPE_OBJECT:
497 case MONO_TYPE_STRING:
498 case MONO_TYPE_CLASS:
499 x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
500 break;
501 case MONO_TYPE_I8:
502 x86_mov_reg_membase (p, X86_EDX, X86_EAX, 4, 4);
503 x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
504 break;
505 case MONO_TYPE_R8:
506 x86_fld_membase (p, X86_EAX, 0, TRUE);
507 break;
508 case MONO_TYPE_VALUETYPE:
509 if (sig->ret->data.klass->enumtype) {
510 simpletype = sig->ret->data.klass->enum_basetype->type;
511 goto enum_retvalue;
514 x86_push_imm (p, sig->pinvoke);
515 x86_push_membase (p, X86_EBP, stackval_pos);
516 x86_push_reg (p, X86_EAX);
517 x86_push_imm (p, sig->ret);
518 x86_mov_reg_imm (p, X86_ECX, stackval_to_data);
519 x86_call_reg (p, X86_ECX);
520 x86_alu_reg_imm (p, X86_SUB, X86_ESP, 16);
522 break;
523 default:
524 g_error ("Type 0x%x not handled yet in thunk creation", sig->ret->type);
525 break;
530 * Standard epilog.
532 x86_leave (p);
533 x86_ret (p);
535 g_assert (p - code_buffer < 512);
537 ji = g_new0 (MonoJitInfo, 1);
538 ji->method = method;
539 ji->code_size = p - code_buffer;
540 ji->code_start = g_memdup (code_buffer, p - code_buffer);
542 mono_jit_info_table_add (mono_get_root_domain (), ji);
544 return ji->code_start;