#include <mono/utils/mono-logger.h>
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-time.h>
+#include <mono/utils/mono-mmap.h>
#include "mini.h"
#include "image-writer.h"
guint32 got_offset, plt_offset, plt_got_offset_base;
/* Number of GOT entries reserved for trampolines */
guint32 num_trampoline_got_entries;
- guint32 num_specific_trampolines;
- guint32 specific_trampoline_size;
- guint32 specific_trampoline_got_offset_base;
- /* Same for static rgctx trampolines */
- guint32 num_static_rgctx_trampolines;
- guint32 static_rgctx_trampoline_size;
- guint32 static_rgctx_trampoline_got_offset_base;
+
+ guint32 num_trampolines [MONO_AOT_TRAMP_NUM];
+ guint32 trampoline_got_offset_base [MONO_AOT_TRAMP_NUM];
+ guint32 trampoline_size [MONO_AOT_TRAMP_NUM];
+
MonoAotOptions aot_opts;
guint32 nmethods;
guint32 opts;
#endif
}
+#ifdef MONO_ARCH_AOT_SUPPORTED
+/*
+ * arch_emit_got_offset:
+ *
+ * The memory pointed to by CODE should hold native code for computing the GOT
+ * address. Emit this code while patching it with the offset between code and
+ * the GOT. CODE_SIZE is set to the number of bytes emitted.
+ */
+static void
+arch_emit_got_offset (MonoAotCompile *acfg, guint8 *code, int *code_size)
+{
+ guint32 offset = mono_arch_get_patch_offset (code);
+ emit_bytes (acfg, code, offset);
+ emit_symbol_diff (acfg, "got", ".", offset);
+
+ *code_size = offset + 4;
+}
+
/*
* arch_emit_got_access:
*
*code_size = mono_arch_get_patch_offset (code) + 4;
}
+#endif
+
/*
* arch_emit_plt_entry:
*
#endif
}
+/*
+ * arch_emit_imt_thunk:
+ *
+ * Emit an IMT thunk usable in full-aot mode. The thunk uses 1 got slot which
+ * points to an array of pointer pairs. The pairs of the form [key, ptr], where
+ * key is the IMT key, and ptr holds the address of a memory location holding
+ * the address to branch to if the IMT arg matches the key. The array is
+ * terminated by a pair whose key is NULL, and whose ptr is the address of the
+ * fail_tramp.
+ * TRAMP_SIZE is set to the size of the emitted trampoline.
+ */
+static void
+arch_emit_imt_thunk (MonoAotCompile *acfg, int offset, int *tramp_size)
+{
+#if defined(TARGET_AMD64)
+ guint8 *buf, *code;
+ guint8 *labels [3];
+
+ code = buf = g_malloc (256);
+
+ /* FIXME: Optimize this, i.e. use binary search etc. */
+ /* Maybe move the body into a separate function (slower, but much smaller) */
+
+ /* R10 is a free register */
+
+ labels [0] = code;
+ amd64_alu_membase_imm (code, X86_CMP, AMD64_R10, 0, 0);
+ labels [1] = code;
+ amd64_branch8 (code, X86_CC_Z, FALSE, 0);
+
+ /* Check key */
+ amd64_alu_membase_reg (code, X86_CMP, AMD64_R10, 0, MONO_ARCH_IMT_REG);
+ labels [2] = code;
+ amd64_branch8 (code, X86_CC_Z, FALSE, 0);
+
+ /* Loop footer */
+ amd64_alu_reg_imm (code, X86_ADD, AMD64_R10, 2 * sizeof (gpointer));
+ amd64_jump_code (code, labels [0]);
+
+ /* Match */
+ mono_amd64_patch (labels [2], code);
+ amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, sizeof (gpointer), 8);
+ amd64_jump_membase (code, AMD64_R10, 0);
+
+ /* No match */
+ /* FIXME: */
+ mono_amd64_patch (labels [1], code);
+ x86_breakpoint (code);
+
+ /* mov <OFFSET>(%rip), %r10 */
+ emit_byte (acfg, '\x4d');
+ emit_byte (acfg, '\x8b');
+ emit_byte (acfg, '\x15');
+ emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) - 4);
+
+ emit_bytes (acfg, buf, code - buf);
+
+ *tramp_size = code - buf + 7;
+#elif defined(TARGET_ARM)
+ guint8 buf [128];
+ guint8 *code, *code2, *labels [16];
+
+ code = buf;
+
+ /* The IMT method is in v5 */
+
+ /* Only IP is available, but we need at least two free registers */
+ ARM_PUSH1 (code, ARMREG_R1);
+ labels [0] = code;
+ /* Load the parameter from the GOT */
+ ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
+ ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
+
+ labels [1] = code;
+ ARM_LDR_IMM (code, ARMREG_R1, ARMREG_IP, 0);
+ ARM_CMP_REG_REG (code, ARMREG_R1, ARMREG_V5);
+ labels [2] = code;
+ ARM_B_COND (code, ARMCOND_EQ, 0);
+
+ /* End-of-loop check */
+ ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
+ labels [3] = code;
+ ARM_B_COND (code, ARMCOND_EQ, 0);
+
+ /* Loop footer */
+ ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, sizeof (gpointer) * 2);
+ labels [4] = code;
+ ARM_B (code, 0);
+ arm_patch (labels [4], labels [1]);
+
+ /* Match */
+ arm_patch (labels [2], code);
+ ARM_POP1 (code, ARMREG_R1);
+ ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 4);
+ ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, 0);
+
+ /* No match */
+ arm_patch (labels [3], code);
+ ARM_DBRK (code);
+
+ /* Fixup offset */
+ code2 = labels [0];
+ ARM_LDR_IMM (code2, ARMREG_IP, ARMREG_PC, (code - (labels [0] + 8)));
+
+ emit_bytes (acfg, buf, code - buf);
+ emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) + (code - (labels [0] + 8)) - 4);
+
+ *tramp_size = code - buf + 4;
+#else
+ g_assert_not_reached ();
+#endif
+}
+
/*
* arch_get_cie_program:
*
}
}
-/*
- * is_shared_got_patch:
- *
- * Return whenever PATCH_INFO refers to a patch which needs a shared GOT
- * entry.
- */
-static inline gboolean
-is_shared_got_patch (MonoJumpInfo *patch_info)
-{
- switch (patch_info->type) {
- case MONO_PATCH_INFO_VTABLE:
- case MONO_PATCH_INFO_CLASS:
- case MONO_PATCH_INFO_IID:
- case MONO_PATCH_INFO_ADJUSTED_IID:
- case MONO_PATCH_INFO_FIELD:
- case MONO_PATCH_INFO_SFLDA:
- case MONO_PATCH_INFO_DECLSEC:
- case MONO_PATCH_INFO_LDTOKEN:
- case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
- case MONO_PATCH_INFO_RVA:
- case MONO_PATCH_INFO_METHODCONST:
- case MONO_PATCH_INFO_IMAGE:
- return TRUE;
- default:
- return FALSE;
- }
-}
-
-gboolean
-mono_aot_is_shared_got_patch (MonoJumpInfo *patch_info)
-{
- return is_shared_got_patch (patch_info);
-}
-
static int
get_plt_offset (MonoAotCompile *acfg, MonoJumpInfo *patch_info)
{
sig = mono_method_signature (method);
- if (sig->hasthis && (method->klass->marshalbyref || method->klass == mono_defaults.object_class) &&
- !(method->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
+ if (sig->hasthis && (method->klass->marshalbyref || method->klass == mono_defaults.object_class)) {
m = mono_marshal_get_remoting_invoke_with_check (method);
add_method (acfg, m);
case MONO_PATCH_INFO_NONE:
break;
case MONO_PATCH_INFO_GOT_OFFSET: {
- guint32 offset = mono_arch_get_patch_offset (code + i);
- emit_bytes (acfg, code + i, offset);
- emit_symbol_diff (acfg, "got", ".", offset);
-
- i += offset + 4 - 1;
+ int code_size;
+
+ arch_emit_got_offset (acfg, code + i, &code_size);
+ i += code_size - 1;
skip = TRUE;
break;
}
emit_alignment (acfg, func_alignment);
emit_label (acfg, symbol);
- if (acfg->aot_opts.write_symbols && !acfg->aot_opts.nodebug && acfg->use_bin_writer) {
- char *full_name;
- /* Emit a local symbol into the symbol table */
- full_name = mono_method_full_name (method, TRUE);
+ if (acfg->aot_opts.write_symbols && !acfg->aot_opts.nodebug) {
+ char *name1, *name2;
+ int i, j, len;
+
+ name1 = mono_method_full_name (method, TRUE);
+ len = strlen (name1);
+ name2 = malloc (len + 1);
+ j = 0;
+ for (i = 0; i < len; ++i) {
+ if (isalnum (name1 [i])) {
+ name2 [j ++] = name1 [i];
+ } else if (name1 [i] == ' ' && name1 [i + 1] == '(' && name1 [i + 2] == ')') {
+ i += 2;
+ } else if (name1 [i] == '(' || name1 [i] == ')') {
+ } else
+ name2 [j ++] = '_';
+ }
+ name2 [j] = '\0';
sprintf (symbol, ".Lme_%x", method_index);
- emit_local_symbol (acfg, full_name, symbol, TRUE);
- emit_label (acfg, full_name);
- g_free (full_name);
+ emit_local_symbol (acfg, name2, symbol, TRUE);
+ emit_label (acfg, name2);
+ g_free (name1);
+ g_free (name2);
}
if (cfg->verbose_level > 0)
continue;
encode_value (patch_info->type, p, &p);
- if (is_shared_got_patch (patch_info)) {
+ if (mono_aot_is_shared_got_patch (patch_info)) {
guint32 offset = get_got_offset (acfg, patch_info);
encode_value (offset, p, &p);
} else {
}
}
-/*
- * When running in aot-only mode, we can't create trampolines at runtime, so we create
- * a few, and save them in the AOT file. Normal trampolines embed their argument as a
- * literal inside the trampoline code, we can't do that here, so instead we embed an offset
- * which needs to be added to the trampoline address to get the address of the GOT slot
- * which contains the argument value.
- * The generated trampolines jump to the generic trampolines using another GOT slot, which
- * will be setup by the AOT loader to point to the generic trampoline code of the given
- * type.
- */
static void
emit_trampolines (MonoAotCompile *acfg)
{
char symbol [256];
int i, tramp_got_offset;
+ MonoAotTrampoline ntype;
#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
int tramp_type;
guint32 code_size;
#endif /* #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES */
+ /* Emit trampolines which are numerous */
+
/*
- * FIXME: Maybe we should use more specific trampolines (i.e. one class init for
- * each class).
+ * These include the following:
+ * - specific trampolines
+ * - static rgctx invoke trampolines
+ * - imt thunks
+ * These trampolines have the same code, they are parameterized by GOT
+ * slots.
+ * They are defined in this file, in the arch_... routines instead of
+ * in tramp-<ARCH>.c, since it is easier to do it this way.
*/
- /* Reserve some entries at the end of the GOT for our use */
- acfg->num_trampoline_got_entries = (acfg->num_specific_trampolines * 2) + (acfg->num_static_rgctx_trampolines * 2);
+ /*
+ * When running in aot-only mode, we can't create specific trampolines at
+ * runtime, so we create a few, and save them in the AOT file.
+ * Normal trampolines embed their argument as a literal inside the
+ * trampoline code, we can't do that here, so instead we embed an offset
+ * which needs to be added to the trampoline address to get the address of
+ * the GOT slot which contains the argument value.
+ * The generated trampolines jump to the generic trampolines using another
+ * GOT slot, which will be setup by the AOT loader to point to the
+ * generic trampoline code of the given type.
+ */
- sprintf (symbol, "specific_trampolines");
+ /*
+ * FIXME: Maybe we should use more specific trampolines (i.e. one class init for
+ * each class).
+ */
emit_section_change (acfg, ".text", 0);
- emit_global (acfg, symbol, TRUE);
- emit_alignment (acfg, 16);
- emit_label (acfg, symbol);
tramp_got_offset = acfg->got_offset;
- acfg->specific_trampoline_got_offset_base = tramp_got_offset;
-
- for (i = 0; i < acfg->num_specific_trampolines; ++i) {
- int tramp_size = 0;
-
- arch_emit_specific_trampoline (acfg, tramp_got_offset, &tramp_size);
- if (!acfg->specific_trampoline_size) {
- g_assert (tramp_size);
- acfg->specific_trampoline_size = tramp_size;
+ for (ntype = 0; ntype < MONO_AOT_TRAMP_NUM; ++ntype) {
+ switch (ntype) {
+ case MONO_AOT_TRAMP_SPECIFIC:
+ sprintf (symbol, "specific_trampolines");
+ break;
+ case MONO_AOT_TRAMP_STATIC_RGCTX:
+ sprintf (symbol, "static_rgctx_trampolines");
+ break;
+ case MONO_AOT_TRAMP_IMT_THUNK:
+ sprintf (symbol, "imt_thunks");
+ break;
+ default:
+ g_assert_not_reached ();
}
- tramp_got_offset += 2;
- }
+ emit_global (acfg, symbol, TRUE);
+ emit_alignment (acfg, 16);
+ emit_label (acfg, symbol);
- sprintf (symbol, "static_rgctx_trampolines");
+ acfg->trampoline_got_offset_base [ntype] = tramp_got_offset;
- emit_section_change (acfg, ".text", 0);
- emit_global (acfg, symbol, TRUE);
- emit_alignment (acfg, 16);
- emit_label (acfg, symbol);
-
- acfg->static_rgctx_trampoline_got_offset_base = tramp_got_offset;
+ for (i = 0; i < acfg->num_trampolines [ntype]; ++i) {
+ int tramp_size = 0;
- for (i = 0; i < acfg->num_static_rgctx_trampolines; ++i) {
- int tramp_size = 0;
+ switch (ntype) {
+ case MONO_AOT_TRAMP_SPECIFIC:
+ arch_emit_specific_trampoline (acfg, tramp_got_offset, &tramp_size);
+ tramp_got_offset += 2;
+ break;
+ case MONO_AOT_TRAMP_STATIC_RGCTX:
+ arch_emit_static_rgctx_trampoline (acfg, tramp_got_offset, &tramp_size);
+ tramp_got_offset += 2;
+ break;
+ case MONO_AOT_TRAMP_IMT_THUNK:
+ arch_emit_imt_thunk (acfg, tramp_got_offset, &tramp_size);
+ tramp_got_offset += 1;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
- arch_emit_static_rgctx_trampoline (acfg, tramp_got_offset, &tramp_size);
- if (!acfg->static_rgctx_trampoline_size) {
- g_assert (tramp_size);
- acfg->static_rgctx_trampoline_size = tramp_size;
+ if (!acfg->trampoline_size [ntype]) {
+ g_assert (tramp_size);
+ acfg->trampoline_size [ntype] = tramp_size;
+ }
}
-
- tramp_got_offset += 2;
}
+
+ /* Reserve some entries at the end of the GOT for our use */
+ acfg->num_trampoline_got_entries = tramp_got_offset - acfg->got_offset;
}
/* Unbox trampolines */
//acfg->aot_opts.print_skipped_methods = TRUE;
- if (acfg->aot_opts.full_aot)
- mono_use_imt = FALSE;
-
/*
* Since these methods are the only ones which are compiled with
* AOT support, and they are not used by runtime startup/shutdown code,
MonoCompile *cfg = acfg->cfgs [i];
for (ji = cfg->patch_info; ji; ji = ji->next) {
- if (is_shared_got_patch (ji))
+ if (mono_aot_is_shared_got_patch (ji))
get_shared_got_offset (acfg, ji);
}
}
return hash;
}
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+#define mix(a,b,c) { \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+#define final(a,b,c) { \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
/*
* mono_aot_method_hash:
*
guint32
mono_aot_method_hash (MonoMethod *method)
{
- guint32 hash;
+ MonoMethodSignature *sig;
+ MonoClass *klass;
+ int i;
+ int hashes_count;
+ guint32 *hashes_start, *hashes;
+ guint32 a, b, c;
- if (method->wrapper_type) {
- hash = mono_aot_str_hash (method->name);
- } else {
- char *full_name = mono_method_full_name (method, TRUE);
- // FIXME: Improve this (changing this requires bumping MONO_AOT_FILE_VERSION)
- hash = mono_aot_str_hash (full_name);
+ /* Similar to the hash in mono_method_get_imt_slot () */
+
+ sig = mono_method_signature (method);
+
+ hashes_count = sig->param_count + 5;
+ hashes_start = malloc (hashes_count * sizeof (guint32));
+ hashes = hashes_start;
+
+ /* Some wrappers are assigned to random classes */
+ if (!method->wrapper_type || method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)
+ klass = method->klass;
+ else
+ klass = mono_defaults.object_class;
+
+ if (!method->wrapper_type) {
+ char *full_name = mono_type_full_name (&klass->byval_arg);
+
+ hashes [0] = mono_aot_str_hash (full_name);
+ hashes [1] = 0;
g_free (full_name);
+ } else {
+ hashes [0] = mono_aot_str_hash (klass->name);
+ hashes [1] = mono_aot_str_hash (klass->name_space);
+ }
+ hashes [2] = mono_aot_str_hash (method->name);
+ hashes [3] = method->wrapper_type;
+ hashes [4] = mono_metadata_type_hash (sig->ret);
+ for (i = 0; i < sig->param_count; i++) {
+ /* This is needed for some reason */
+ if (method->wrapper_type && sig->params [i]->type == MONO_TYPE_GENERICINST)
+ hashes [5 + i] = MONO_TYPE_GENERICINST;
+ else
+ hashes [5 + i] = mono_metadata_type_hash (sig->params [i]);
+ }
+
+ /* Setup internal state */
+ a = b = c = 0xdeadbeef + (((guint32)hashes_count)<<2);
+
+ /* Handle most of the hashes */
+ while (hashes_count > 3) {
+ a += hashes [0];
+ b += hashes [1];
+ c += hashes [2];
+ mix (a,b,c);
+ hashes_count -= 3;
+ hashes += 3;
}
- return hash;
+ /* Handle the last 3 hashes (all the case statements fall through) */
+ switch (hashes_count) {
+ case 3 : c += hashes [2];
+ case 2 : b += hashes [1];
+ case 1 : a += hashes [0];
+ final (a,b,c);
+ case 0: /* nothing left to add */
+ break;
+ }
+
+ free (hashes_start);
+
+ return c;
}
+#undef rot
+#undef mix
+#undef final
/*
* mono_aot_wrapper_name:
* Create a MonoAotTrampInfo structure from the arguments.
*/
MonoAotTrampInfo*
-mono_aot_tramp_info_create (char *name, guint8 *code, guint32 code_size)
+mono_aot_tramp_info_create (const char *name, guint8 *code, guint32 code_size)
{
MonoAotTrampInfo *info = g_new0 (MonoAotTrampInfo, 1);
- info->name = name;
+ info->name = (char*)name;
info->code = code;
info->code_size = code_size;
return info;
}
+/*
+ * mono_is_shared_got_patch:
+ *
+ * Return whenever PATCH_INFO refers to a patch which needs a shared GOT
+ * entry.
+ */
+gboolean
+mono_aot_is_shared_got_patch (MonoJumpInfo *patch_info)
+{
+ switch (patch_info->type) {
+ case MONO_PATCH_INFO_VTABLE:
+ case MONO_PATCH_INFO_CLASS:
+ case MONO_PATCH_INFO_IID:
+ case MONO_PATCH_INFO_ADJUSTED_IID:
+ case MONO_PATCH_INFO_FIELD:
+ case MONO_PATCH_INFO_SFLDA:
+ case MONO_PATCH_INFO_DECLSEC:
+ case MONO_PATCH_INFO_LDTOKEN:
+ case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
+ case MONO_PATCH_INFO_RVA:
+ case MONO_PATCH_INFO_METHODCONST:
+ case MONO_PATCH_INFO_IMAGE:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
#if !defined(DISABLE_AOT) && !defined(DISABLE_JIT)
typedef struct HashEntry {
emit_label (acfg, symbol);
if (acfg->got_offset > 0)
emit_zero_bytes (acfg, (int)(acfg->got_offset * sizeof (gpointer)));
+
+ sprintf (symbol, "mono_aot_got_addr");
+ emit_section_change (acfg, ".data", 0);
+ emit_global (acfg, symbol, FALSE);
+ emit_alignment (acfg, 8);
+ emit_label (acfg, symbol);
+ emit_pointer (acfg, "got");
}
static void
emit_file_info (MonoAotCompile *acfg)
{
char symbol [128];
+ int i;
sprintf (symbol, "mono_aot_file_info");
emit_section_change (acfg, ".data", 0);
emit_int32 (acfg, acfg->plt_got_offset_base);
emit_int32 (acfg, (int)(acfg->got_offset * sizeof (gpointer)));
emit_int32 (acfg, acfg->plt_offset);
- emit_int32 (acfg, acfg->num_specific_trampolines);
- emit_int32 (acfg, acfg->specific_trampoline_size);
- emit_int32 (acfg, acfg->specific_trampoline_got_offset_base);
- emit_int32 (acfg, acfg->num_static_rgctx_trampolines);
- emit_int32 (acfg, acfg->static_rgctx_trampoline_size);
- emit_int32 (acfg, acfg->static_rgctx_trampoline_got_offset_base);
- emit_pointer (acfg, "got");
+
+ for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
+ emit_int32 (acfg, acfg->num_trampolines [i]);
+ for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
+ emit_int32 (acfg, acfg->trampoline_got_offset_base [i]);
+ for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
+ emit_int32 (acfg, acfg->trampoline_size [i]);
}
static void
if (!acfg->aot_opts.nodebug)
acfg->dwarf = mono_dwarf_writer_create (acfg->w, NULL);
- acfg->num_specific_trampolines = acfg->aot_opts.full_aot ? acfg->aot_opts.ntrampolines : 0;
+ acfg->num_trampolines [MONO_AOT_TRAMP_SPECIFIC] = acfg->aot_opts.full_aot ? acfg->aot_opts.ntrampolines : 0;
#ifdef MONO_ARCH_HAVE_STATIC_RGCTX_TRAMPOLINE
- acfg->num_static_rgctx_trampolines = acfg->aot_opts.full_aot ? 1024 : 0;
+ acfg->num_trampolines [MONO_AOT_TRAMP_STATIC_RGCTX] = acfg->aot_opts.full_aot ? 1024 : 0;
#endif
+ acfg->num_trampolines [MONO_AOT_TRAMP_IMT_THUNK] = acfg->aot_opts.full_aot ? 128 : 0;
acfg->method_index = 1;