2 * Create trampolines to invoke arbitrary functions.
3 * Copyright (c) 2002 Sergey Chaban <serge@wildwestsoftware.com>
5 * Contributions by Malte Hildingson
8 #include "arm-codegen.h"
11 #if defined(_WIN32_WCE) || defined (UNDER_CE)
18 #if !defined(PLATFORM_MACOSX)
21 #include "mono/metadata/class.h"
22 #include "mono/metadata/tabledefs.h"
23 #include "mono/interpreter/interp.h"
24 #include "mono/metadata/appdomain.h"
28 # define ARM_DUMP_DISASM 1
31 /* prototypes for private functions (to avoid compiler warnings) */
32 void flush_icache (void);
33 void* alloc_code_buff (int num_instr);
38 * The resulting function takes the form:
39 * void func (void (*callme)(), void *retval, void *this_obj, stackval *arguments);
40 * NOTE: all args passed in ARM registers (A1-A4),
41 * then copied to R4-R7 (see definitions below).
44 #define REG_FUNC_ADDR ARMREG_R4
45 #define REG_RETVAL ARMREG_R5
46 #define REG_THIS ARMREG_R6
47 #define REG_ARGP ARMREG_R7
50 #define ARG_SIZE sizeof(stackval)
58 FlushInstructionCache(GetCurrentProcess(), NULL, 0);
63 asm ("mcr p15, 0, r0, c7, c7, 0");
65 /* TODO: use (movnv pc, rx) method */
71 void* alloc_code_buff (int num_instr)
74 int code_size = num_instr * sizeof(arminstr_t);
76 #if defined(_WIN32) || defined(UNDER_CE)
79 code_buff = malloc(code_size);
80 VirtualProtect(code_buff, code_size, PAGE_EXECUTE_READWRITE, &old_prot);
82 int page_size = sysconf(_SC_PAGESIZE);
85 new_code_size = code_size + page_size - 1;
86 code_buff = malloc(new_code_size);
87 code_buff = (void *) (((int) code_buff + page_size - 1) & ~(page_size - 1));
89 if (mprotect(code_buff, code_size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0) {
90 g_critical (G_GNUC_PRETTY_FUNCTION
91 ": mprotect error: %s", g_strerror (errno));
100 * Refer to ARM Procedure Call Standard (APCS) for more info.
102 MonoPIFunc mono_arch_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor)
105 MonoPIFunc code_buff;
107 guint32 code_size, stack_size;
109 int i, hasthis, aregs, regc, stack_offs;
111 guchar reg_alloc [ARM_NUM_ARG_REGS];
113 /* pessimistic estimation for prologue/epilogue size */
115 /* push/pop work regs */
123 hasthis = sig->hasthis ? 1 : 0;
125 aregs = ARM_NUM_ARG_REGS - hasthis;
127 for (i = 0, regc = aregs; i < sig->param_count; ++i) {
128 param = sig->params [i];
130 /* keep track of argument sizes */
131 if (i < ARM_NUM_ARG_REGS) reg_alloc [i] = 0;
136 reg_alloc [i] = regc;
140 stack_size += sizeof(gpointer);
143 simple_type = param->type;
145 switch (simple_type) {
146 case MONO_TYPE_BOOLEAN:
158 case MONO_TYPE_SZARRAY:
159 case MONO_TYPE_CLASS:
160 case MONO_TYPE_OBJECT:
161 case MONO_TYPE_STRING:
165 reg_alloc [i] = regc;
176 /* keep track of argument sizes */
178 /* fits into registers, two LDRs */
180 reg_alloc [i] = regc;
182 } else if (regc > 0) {
183 /* first half fits into register, one LDR */
185 reg_alloc [i] = regc;
187 /* the rest on the stack, LDR/STR */
191 /* stack arg, 4 instrs - 2x(LDR/STR) */
196 case MONO_TYPE_VALUETYPE:
197 if (param->data.klass->enumtype) {
198 simple_type = param->data.klass->enum_basetype->type;
202 if (mono_class_value_size(param->data.klass, NULL) != 4) {
203 g_error("can only marshal enums, not generic structures (size: %d)", mono_class_value_size(param->data.klass, NULL));
208 reg_alloc [i] = regc;
222 code_buff = (MonoPIFunc)alloc_code_buff(code_size);
223 p = (arminstr_t*)code_buff;
226 p = arm_emit_lean_prologue(p, stack_size,
227 /* save workset (r4-r7) */
228 (1 << ARMREG_R4) | (1 << ARMREG_R5) | (1 << ARMREG_R6) | (1 << ARMREG_R7));
231 /* copy args into workset */
232 /* callme - always present */
233 ARM_MOV_REG_REG(p, ARMREG_R4, ARMREG_A1);
235 if (sig->ret->byref || string_ctor || (sig->ret->type != MONO_TYPE_VOID)) {
236 ARM_MOV_REG_REG(p, ARMREG_R5, ARMREG_A2);
241 if (stack_size == 0) {
242 ARM_MOV_REG_REG(p, ARMREG_A1, ARMREG_A3);
245 ARM_MOV_REG_REG(p, ARMREG_R6, ARMREG_A3);
249 if (sig->param_count != 0) {
250 ARM_MOV_REG_REG(p, ARMREG_R7, ARMREG_A4);
253 stack_offs = stack_size;
255 /* handle arguments */
256 /* in reverse order so we could use r0 (arg1) for memory transfers */
257 for (i = sig->param_count; --i >= 0;) {
258 param = sig->params [i];
260 if (i < aregs && reg_alloc[i] > 0) {
261 ARM_LDR_IMM(p, ARMREG_A1 + i, REG_ARGP, i*ARG_SIZE);
263 stack_offs -= sizeof(armword_t);
264 ARM_LDR_IMM(p, ARMREG_R0, REG_ARGP, i*ARG_SIZE);
265 ARM_STR_IMM(p, ARMREG_R0, ARMREG_SP, stack_offs);
268 simple_type = param->type;
270 switch (simple_type) {
271 case MONO_TYPE_BOOLEAN:
283 case MONO_TYPE_SZARRAY:
284 case MONO_TYPE_CLASS:
285 case MONO_TYPE_OBJECT:
286 case MONO_TYPE_STRING:
287 if (i < aregs && reg_alloc [i] > 0) {
288 /* pass in register */
289 ARM_LDR_IMM(p, ARMREG_A1 + hasthis + (aregs - reg_alloc [i]), REG_ARGP, i*ARG_SIZE);
291 stack_offs -= sizeof(armword_t);
292 ARM_LDR_IMM(p, ARMREG_R0, REG_ARGP, i*ARG_SIZE);
293 ARM_STR_IMM(p, ARMREG_R0, ARMREG_SP, stack_offs);
299 if (i < aregs && reg_alloc [i] > 0) {
300 if (reg_alloc [i] > 1) {
301 /* pass in registers */
302 ARM_LDR_IMM(p, ARMREG_A1 + hasthis + (aregs - reg_alloc [i]), REG_ARGP, i*ARG_SIZE);
303 ARM_LDR_IMM(p, ARMREG_A1 + hasthis + (aregs - reg_alloc [i]) + 1, REG_ARGP, i*ARG_SIZE + 4);
305 stack_offs -= sizeof(armword_t);
306 ARM_LDR_IMM(p, ARMREG_R0, REG_ARGP, i*ARG_SIZE + 4);
307 ARM_STR_IMM(p, ARMREG_R0, ARMREG_SP, stack_offs);
308 ARM_LDR_IMM(p, ARMREG_A1 + hasthis + (aregs - reg_alloc [i]), REG_ARGP, i*ARG_SIZE);
311 /* two words transferred on the stack */
312 stack_offs -= 2*sizeof(armword_t);
313 ARM_LDR_IMM(p, ARMREG_R0, REG_ARGP, i*ARG_SIZE);
314 ARM_STR_IMM(p, ARMREG_R0, ARMREG_SP, stack_offs);
315 ARM_LDR_IMM(p, ARMREG_R0, REG_ARGP, i*ARG_SIZE + 4);
316 ARM_STR_IMM(p, ARMREG_R0, ARMREG_SP, stack_offs + 4);
319 case MONO_TYPE_VALUETYPE:
320 if (param->data.klass->enumtype) {
321 /* it's an enum value, proceed based on its base type */
322 simple_type = param->data.klass->enum_basetype->type;
325 if (i < aregs && reg_alloc[i] > 0) {
326 int vtreg = ARMREG_A1 + hasthis +
327 hasthis + (aregs - reg_alloc[i]);
328 ARM_LDR_IMM(p, vtreg, REG_ARGP, i * ARG_SIZE);
329 ARM_LDR_IMM(p, vtreg, vtreg, 0);
331 stack_offs -= sizeof(armword_t);
332 ARM_LDR_IMM(p, ARMREG_R0, REG_ARGP, i * ARG_SIZE);
333 ARM_LDR_IMM(p, ARMREG_R0, ARMREG_R0, 0);
334 ARM_STR_IMM(p, ARMREG_R0, ARMREG_SP, stack_offs);
345 if (sig->hasthis && !this_loaded) {
346 /* [this] always passed in A1, regardless of sig->call_convention */
347 ARM_MOV_REG_REG(p, ARMREG_A1, REG_THIS);
351 ARM_MOV_REG_REG(p, ARMREG_LR, ARMREG_PC);
352 ARM_MOV_REG_REG(p, ARMREG_PC, REG_FUNC_ADDR);
355 if (sig->ret->byref || string_ctor) {
356 ARM_STR_IMM(p, ARMREG_R0, REG_RETVAL, 0);
358 simple_type = sig->ret->type;
360 switch (simple_type) {
361 case MONO_TYPE_BOOLEAN:
364 ARM_STRB_IMM(p, ARMREG_R0, REG_RETVAL, 0);
369 ARM_STRH_IMM(p, ARMREG_R0, REG_RETVAL, 0);
372 * A 32-bit integer and integer-equivalent return value
374 * Single-precision floating-point values are returned in R0.
381 case MONO_TYPE_OBJECT:
382 case MONO_TYPE_CLASS:
383 case MONO_TYPE_ARRAY:
384 case MONO_TYPE_SZARRAY:
385 case MONO_TYPE_STRING:
386 ARM_STR_IMM(p, ARMREG_R0, REG_RETVAL, 0);
389 * A 64-bit integer is returned in R0 and R1.
390 * Double-precision floating-point values are returned in R0 and R1.
395 ARM_STR_IMM(p, ARMREG_R0, REG_RETVAL, 0);
396 ARM_STR_IMM(p, ARMREG_R1, REG_RETVAL, 4);
398 case MONO_TYPE_VALUETYPE:
399 if (sig->ret->data.klass->enumtype) {
400 simple_type = sig->ret->data.klass->enum_basetype->type;
411 p = arm_emit_std_epilogue(p, stack_size,
413 (1 << ARMREG_R4) | (1 << ARMREG_R5) | (1 << ARMREG_R6) | (1 << ARMREG_R7));
417 #ifdef ARM_DUMP_DISASM
418 _armdis_decode((arminstr_t*)code_buff, ((guint8*)p) - ((guint8*)code_buff));
426 #define MINV_OFFS(member) G_STRUCT_OFFSET(MonoInvocation, member)
431 * Returns a pointer to a native function that can be used to
432 * call the specified method.
433 * The function created will receive the arguments according
434 * to the call convention specified in the method.
435 * This function works by creating a MonoInvocation structure,
436 * filling the fields in and calling ves_exec_method on it.
437 * Still need to figure out how to handle the exception stuff
438 * across the managed/unmanaged boundary.
440 void* mono_arch_create_method_pointer (MonoMethod* method)
442 MonoMethodSignature* sig;
443 guchar* p, * p_method, * p_stackval_from_data, * p_exec;
445 int i, stack_size, arg_pos, arg_add, stackval_pos, offs;
446 int areg, reg_args, shift, pos;
449 code_buff = alloc_code_buff(128);
450 p = (guchar*)code_buff;
452 sig = method->signature;
456 /* embed magic number followed by method pointer */
467 *(void**)p = stackval_from_data;
468 p_stackval_from_data = p;
470 *(void**)p = ves_exec_method;
474 stack_size = sizeof(MonoInvocation) + ARG_SIZE*(sig->param_count + 1) + ARM_NUM_ARG_REGS*2*sizeof(armword_t);
477 p = (guchar*)arm_emit_lean_prologue((arminstr_t*)p, stack_size,
483 /* R7 - ptr to stack args */
484 ARM_MOV_REG_REG(p, ARMREG_R7, ARMREG_IP);
487 * Initialize MonoInvocation fields, first the ones known now.
489 ARM_MOV_REG_IMM8(p, ARMREG_R4, 0);
490 ARM_STR_IMM(p, ARMREG_R4, ARMREG_SP, MINV_OFFS(ex));
491 ARM_STR_IMM(p, ARMREG_R4, ARMREG_SP, MINV_OFFS(ex_handler));
492 ARM_STR_IMM(p, ARMREG_R4, ARMREG_SP, MINV_OFFS(parent));
494 /* Set the method pointer. */
495 ARM_LDR_IMM(p, ARMREG_R4, ARMREG_PC, -(int)(p - p_method + sizeof(arminstr_t)*2));
496 ARM_STR_IMM(p, ARMREG_R4, ARMREG_SP, MINV_OFFS(method));
500 ARM_STR_IMM(p, ARMREG_A1, ARMREG_SP, MINV_OFFS(obj));
502 /* else set minv.obj to NULL */
503 ARM_STR_IMM(p, ARMREG_R4, ARMREG_SP, MINV_OFFS(obj));
506 /* copy args from registers to stack */
507 areg = ARMREG_A1 + sig->hasthis;
508 arg_pos = -(int)(ARM_NUM_ARG_REGS - sig->hasthis) * 2 * sizeof(armword_t);
510 for (i = 0; i < sig->param_count; ++i) {
511 if (areg >= ARM_NUM_ARG_REGS) break;
512 ARM_STR_IMM(p, areg, ARMREG_R7, arg_pos);
514 if (!sig->params[i]->byref) {
515 switch (sig->params[i]->type) {
519 if (areg >= ARM_NUM_ARG_REGS) {
520 /* load second half of 64-bit arg */
521 ARM_LDR_IMM(p, ARMREG_R4, ARMREG_R7, 0);
522 ARM_STR_IMM(p, ARMREG_R4, ARMREG_R7, arg_pos + sizeof(armword_t));
523 arg_add = sizeof(armword_t);
525 /* second half is already the register */
526 ARM_STR_IMM(p, areg, ARMREG_R7, arg_pos + sizeof(armword_t));
530 case MONO_TYPE_VALUETYPE:
536 arg_pos += 2 * sizeof(armword_t);
538 /* number of args passed in registers */
544 * Calc and save stack args ptr,
545 * args follow MonoInvocation struct on the stack.
547 ARM_ADD_REG_IMM8(p, ARMREG_R1, ARMREG_SP, sizeof(MonoInvocation));
548 ARM_STR_IMM(p, ARMREG_R1, ARMREG_SP, MINV_OFFS(stack_args));
550 /* convert method args to stackvals */
551 arg_pos = -(int)(ARM_NUM_ARG_REGS - sig->hasthis) * 2 * sizeof(armword_t);
552 stackval_pos = sizeof(MonoInvocation);
553 for (i = 0; i < sig->param_count; ++i) {
555 ARM_SUB_REG_IMM8(p, ARMREG_A3, ARMREG_R7, -arg_pos);
556 arg_pos += 2 * sizeof(armword_t);
558 if (arg_pos < 0) arg_pos = 0;
559 pos = arg_pos + arg_add;
561 ARM_ADD_REG_IMM8(p, ARMREG_A3, ARMREG_R7, pos);
563 if (is_arm_const((armword_t)pos)) {
564 shift = calc_arm_mov_const_shift((armword_t)pos);
565 ARM_ADD_REG_IMM(p, ARMREG_A3, ARMREG_R7, pos >> ((32 - shift) & 31), shift >> 1);
567 p = (guchar*)arm_mov_reg_imm32((arminstr_t*)p, ARMREG_R6, (armword_t)pos);
568 ARM_ADD_REG_REG(p, ARMREG_A2, ARMREG_R7, ARMREG_R6);
571 arg_pos += sizeof(armword_t);
572 if (!sig->params[i]->byref) {
573 switch (sig->params[i]->type) {
577 arg_pos += sizeof(armword_t);
579 case MONO_TYPE_VALUETYPE:
588 if (stackval_pos <= 0xFF) {
589 ARM_ADD_REG_IMM8(p, ARMREG_A2, ARMREG_SP, stackval_pos);
591 if (is_arm_const((armword_t)stackval_pos)) {
592 shift = calc_arm_mov_const_shift((armword_t)stackval_pos);
593 ARM_ADD_REG_IMM(p, ARMREG_A2, ARMREG_SP, stackval_pos >> ((32 - shift) & 31), shift >> 1);
595 p = (guchar*)arm_mov_reg_imm32((arminstr_t*)p, ARMREG_R6, (armword_t)stackval_pos);
596 ARM_ADD_REG_REG(p, ARMREG_A2, ARMREG_SP, ARMREG_R6);
601 p = (guchar*)arm_mov_reg_imm32((arminstr_t*)p, ARMREG_A1, (armword_t)sig->params [i]);
603 stackval_pos += ARG_SIZE;
605 offs = -(p + 2*sizeof(arminstr_t) - p_stackval_from_data);
606 /* load function address */
607 ARM_LDR_IMM(p, ARMREG_R4, ARMREG_PC, offs);
608 /* call stackval_from_data */
609 ARM_MOV_REG_REG(p, ARMREG_LR, ARMREG_PC);
610 ARM_MOV_REG_REG(p, ARMREG_PC, ARMREG_R4);
613 /* store retval ptr */
614 p = (guchar*)arm_mov_reg_imm32((arminstr_t*)p, ARMREG_R5, (armword_t)stackval_pos);
615 ARM_ADD_REG_REG(p, ARMREG_R5, ARMREG_SP, ARMREG_R4);
616 ARM_STR_IMM(p, ARMREG_R5, ARMREG_SP, MINV_OFFS(retval));
621 /* A1 = MonoInvocation ptr */
622 ARM_MOV_REG_REG(p, ARMREG_A1, ARMREG_SP);
623 offs = -(p + 2*sizeof(arminstr_t) - p_exec);
624 /* load function address */
625 ARM_LDR_IMM(p, ARMREG_R4, ARMREG_PC, offs);
627 ARM_MOV_REG_REG(p, ARMREG_LR, ARMREG_PC);
628 ARM_MOV_REG_REG(p, ARMREG_PC, ARMREG_R4);
632 * Move retval into reg.
634 if (sig->ret->byref) {
635 ARM_LDR_IMM(p, ARMREG_R0, ARMREG_R5, 0);
637 switch (sig->ret->type) {
638 case MONO_TYPE_BOOLEAN:
641 ARM_LDRB_IMM(p, ARMREG_R0, ARMREG_R5, 0);
646 ARM_LDRH_IMM(p, ARMREG_R0, ARMREG_R5, 0);
653 case MONO_TYPE_OBJECT:
654 case MONO_TYPE_CLASS:
655 case MONO_TYPE_ARRAY:
656 case MONO_TYPE_SZARRAY:
657 ARM_LDR_IMM(p, ARMREG_R0, ARMREG_R5, 0);
662 ARM_LDR_IMM(p, ARMREG_R0, ARMREG_R5, 0);
663 ARM_LDR_IMM(p, ARMREG_R1, ARMREG_R5, 4);
672 p = (guchar*)arm_emit_std_epilogue((arminstr_t*)p, stack_size,
680 #ifdef ARM_DUMP_DISASM
681 _armdis_decode((arminstr_t*)code_buff, ((guint8*)p) - ((guint8*)code_buff));
684 ji = g_new0(MonoJitInfo, 1);
686 ji->code_size = ((guint8 *) p) - ((guint8 *) code_buff);
687 ji->code_start = (gpointer) code_buff;
689 mono_jit_info_table_add(mono_get_root_domain (), ji);
696 * mono_create_method_pointer () will insert a pointer to the MonoMethod
697 * so that the interp can easily get at the data: this function will retrieve
698 * the method from the code stream.
700 MonoMethod* mono_method_pointer_get (void* code)
702 unsigned char* c = code;
703 /* check out magic number that follows unconditional branch */
707 c[7] == 'o') return ((MonoMethod**)code)[2];