Merge pull request #498 from Unroll-Me/master
[mono.git] / mono / mini / aot-runtime.c
index 7122bf743c15d2d6cfe404d87c7bc1f57fe4e29e..2f67e93ec11aa9b4c53b760c4711bce388839276 100644 (file)
@@ -98,6 +98,9 @@ typedef struct MonoAotModule {
        guint8 *plt_end;
        guint8 *blob;
        gint32 *code_offsets;
+#ifdef MONOTOUCH
+       gpointer *method_addresses;
+#endif
        /* This contains <offset, index> pairs sorted by offset */
        /* This is needed because LLVM emitted methods can be in any order */
        gint32 *sorted_code_offsets;
@@ -251,7 +254,7 @@ decode_value (guint8 *ptr, guint8 **rptr)
 }
 
 /*
- * mono_aot_get_method:
+ * mono_aot_get_offset:
  *
  *   Decode an offset table emitted by emit_offset_table (), returning the INDEXth
  * entry.
@@ -415,35 +418,52 @@ decode_klass_ref (MonoAotModule *module, guint8 *buf, guint8 **endbuf)
        }
        case MONO_AOT_TYPEREF_VAR: {
                MonoType *t;
-               MonoGenericContainer *container;
+               MonoGenericContainer *container = NULL;
                int type = decode_value (p, &p);
                int num = decode_value (p, &p);
-               gboolean is_method = decode_value (p, &p);
+               gboolean has_container = decode_value (p, &p);
+               int serial = 0;
 
-               if (is_method) {
-                       MonoMethod *method_def;
-                       g_assert (type == MONO_TYPE_MVAR);
-                       method_def = decode_resolve_method_ref (module, p, &p);
-                       if (!method_def)
-                               return NULL;
-
-                       container = mono_method_get_generic_container (method_def);
+               if (has_container) {
+                       gboolean is_method = decode_value (p, &p);
+                       
+                       if (is_method) {
+                               MonoMethod *method_def;
+                               g_assert (type == MONO_TYPE_MVAR);
+                               method_def = decode_resolve_method_ref (module, p, &p);
+                               if (!method_def)
+                                       return NULL;
+
+                               container = mono_method_get_generic_container (method_def);
+                       } else {
+                               MonoClass *class_def;
+                               g_assert (type == MONO_TYPE_VAR);
+                               class_def = decode_klass_ref (module, p, &p);
+                               if (!class_def)
+                                       return NULL;
+                               container = class_def->generic_container;
+                       }
                } else {
-                       MonoClass *class_def;
-                       g_assert (type == MONO_TYPE_VAR);
-                       class_def = decode_klass_ref (module, p, &p);
-                       if (!class_def)
-                               return NULL;
-
-                       container = class_def->generic_container;
+                       serial = decode_value (p, &p);
                }
 
-               g_assert (container);
-
                // FIXME: Memory management
                t = g_new0 (MonoType, 1);
                t->type = type;
-               t->data.generic_param = mono_generic_container_get_param (container, num);
+
+               if (container) {
+                       t->data.generic_param = mono_generic_container_get_param (container, num);
+                       g_assert (serial == 0);
+               } else {
+                       /* Anonymous */
+                       MonoGenericParam *par = (MonoGenericParam*)g_new0 (MonoGenericParamFull, 1);
+                       par->num = num;
+                       par->serial = serial;
+                       // FIXME:
+                       par->image = mono_defaults.corlib;
+                       t->data.generic_param = par;
+               }
 
                // FIXME: Maybe use types directly to avoid
                // the overhead of creating MonoClass-es
@@ -804,7 +824,8 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod
                                kind = decode_value (p, &p);
 
                                /* Can't decode this */
-                               g_assert (target);
+                               if (!target)
+                                       return FALSE;
                                if (target->wrapper_type == MONO_WRAPPER_STELEMREF) {
                                        info = mono_marshal_get_wrapper_info (target);
 
@@ -840,7 +861,8 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod
                                if (!klass)
                                        return FALSE;
 
-                               g_assert (target);
+                               if (!target)
+                                       return FALSE;
                                if (klass != target->klass)
                                        return FALSE;
 
@@ -853,6 +875,12 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod
                                                return FALSE;
                                        ref->method = mono_marshal_get_struct_to_ptr (klass);
                                }
+                       } else if (subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER) {
+                               MonoMethod *m = decode_resolve_method_ref (module, p, &p);
+
+                               if (!m)
+                                       return FALSE;
+                               ref->method = mono_marshal_get_synchronized_inner_wrapper (m);
                        } else {
                                if (subtype == WRAPPER_SUBTYPE_FAST_MONITOR_ENTER)
                                        desc = mono_method_desc_new ("Monitor:Enter", FALSE);
@@ -885,7 +913,8 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod
                                if (!m)
                                        return FALSE;
 
-                               g_assert (target);
+                               if (!target)
+                                       return FALSE;
                                g_assert (target->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED);
 
                                info = mono_marshal_get_wrapper_info (target);
@@ -902,7 +931,8 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod
                        char *name;
 
                        if (subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER) {
-                               g_assert (target);
+                               if (!target)
+                                       return FALSE;
 
                                name = (char*)p;
                                if (strcmp (target->name, name) != 0)
@@ -915,7 +945,8 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod
                                        return FALSE;
 
                                /* This should only happen when looking for an extra method */
-                               g_assert (target);
+                               if (!target)
+                                       return FALSE;
                                if (mono_marshal_method_from_wrapper (target) == m)
                                        ref->method = target;
                                else
@@ -937,7 +968,8 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod
                case MONO_WRAPPER_RUNTIME_INVOKE: {
                        int subtype = decode_value (p, &p);
 
-                       g_assert (target);
+                       if (!target)
+                               return FALSE;
 
                        if (subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC) {
                                if (strcmp (target->name, "runtime_invoke_dynamic") != 0)
@@ -972,7 +1004,8 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod
                         * These wrappers are associated with a signature, not with a method.
                         * Since we can't decode them into methods, they need a target method.
                         */
-                       g_assert (target);
+                       if (!target)
+                               return FALSE;
 
                        if (sig_matches_target (module, target, p, &p))
                                ref->method = target;
@@ -1306,6 +1339,7 @@ check_usable (MonoAssembly *assembly, MonoAotFileInfo *info, char **out_msg)
        gboolean usable = TRUE;
        gboolean full_aot;
        guint8 *blob;
+       guint32 excluded_cpu_optimizations;
 
        if (strcmp (assembly->image->guid, info->assembly_guid)) {
                msg = g_strdup_printf ("doesn't match assembly");
@@ -1345,6 +1379,17 @@ check_usable (MonoAssembly *assembly, MonoAotFileInfo *info, char **out_msg)
                usable = FALSE;
        }
 
+       mono_arch_cpu_optimizations (&excluded_cpu_optimizations);
+       if (info->opts & excluded_cpu_optimizations) {
+               msg = g_strdup_printf ("compiled with unsupported CPU optimizations");
+               usable = FALSE;
+       }
+
+       if (!mono_aot_only && (info->simd_opts & ~mono_arch_cpu_enumerate_simd_versions ())) {
+               msg = g_strdup_printf ("compiled with unsupported SIMD extensions");
+               usable = FALSE;
+       }
+
        blob = info->blob;
 
        if (info->gc_name_index != -1) {
@@ -1422,7 +1467,7 @@ load_aot_module (MonoAssembly *assembly, gpointer user_data)
        }
 
        if (!sofile && !globals) {
-               if (mono_aot_only) {
+               if (mono_aot_only && assembly->image->tables [MONO_TABLE_METHOD].rows) {
                        fprintf (stderr, "Failed to load AOT module '%s' in aot-only mode.\n", aot_name);
                        exit (1);
                }
@@ -1526,6 +1571,9 @@ load_aot_module (MonoAssembly *assembly, gpointer user_data)
        }
 
        amodule->code_offsets = info->code_offsets;
+#ifdef MONOTOUCH
+       amodule->method_addresses = info->method_addresses;
+#endif
        amodule->code = info->methods;
 #ifdef TARGET_ARM
        /* Mask out thumb interop bit */
@@ -1552,6 +1600,19 @@ load_aot_module (MonoAssembly *assembly, gpointer user_data)
        amodule->trampolines [MONO_AOT_TRAMP_IMT_THUNK] = info->imt_thunks;
        amodule->thumb_end = info->thumb_end;
 
+#ifdef MONOTOUCH
+       if (info->flags & MONO_AOT_FILE_FLAG_DIRECT_METHOD_ADDRESSES) {
+               /* Compute code_offsets from the method addresses */
+               amodule->code_offsets = g_malloc0 (amodule->info.nmethods * sizeof (gint32));
+               for (i = 0; i < amodule->info.nmethods; ++i) {
+                       if (!amodule->method_addresses [i])
+                               amodule->code_offsets [i] = 0xffffffff;
+                       else
+                               amodule->code_offsets [i] = (char*)amodule->method_addresses [i] - (char*)amodule->code;
+               }
+       }
+#endif
+
        if (make_unreadable) {
 #ifndef TARGET_WIN32
                guint8 *addr;
@@ -1782,7 +1843,7 @@ mono_aot_get_method_from_vt_slot (MonoDomain *domain, MonoVTable *vtable, int sl
        if (ref.no_aot_trampoline)
                return NULL;
 
-       if (mono_metadata_token_index (ref.token) == 0)
+       if (mono_metadata_token_index (ref.token) == 0 || mono_metadata_token_table (ref.token) != MONO_TABLE_METHOD)
                return NULL;
 
        return mono_aot_get_method_from_token (domain, ref.image, ref.token);
@@ -1933,7 +1994,7 @@ decode_llvm_mono_eh_frame (MonoAotModule *amodule, MonoDomain *domain,
        guint8 *fde, *cie, *code_start, *code_end;
        int version, fde_count;
        gint32 *table;
-       int i, j, pos, left, right, offset, offset1, offset2, code_len;
+       int i, j, pos, left, right, offset, offset1, offset2, code_len, func_encoding;
        MonoJitExceptionInfo *ei;
        guint32 fde_len, ei_len, nested_len, nindex;
        gpointer *type_info;
@@ -1948,8 +2009,14 @@ decode_llvm_mono_eh_frame (MonoAotModule *amodule, MonoDomain *domain,
 
        /* Header */
        version = *p;
-       g_assert (version == 1);
+       g_assert (version == 1 || version == 2);
        p ++;
+       if (version == 2) {
+               func_encoding = *p;
+               p ++;
+       } else {
+               func_encoding = DW_EH_PE_pcrel;
+       }
        p = ALIGN_PTR_TO (p, 4);
 
        fde_count = *(guint32*)p;
@@ -1968,11 +2035,23 @@ decode_llvm_mono_eh_frame (MonoAotModule *amodule, MonoDomain *domain,
                pos = (left + right) / 2;
 
                offset1 = table [(pos * 2)];
-               if (pos + 1 == fde_count)
+               if (pos + 1 == fde_count) {
                        /* FIXME: */
                        offset2 = amodule->code_end - amodule->code;
-               else
+               } else {
+                       /* Encoded as DW_EH_PE_pcrel, but relative to mono_eh_frame */
                        offset2 = table [(pos + 1) * 2];
+               }
+
+               if (func_encoding == DW_EH_PE_absptr) {
+                       /*
+                        * Encoded as DW_EH_PE_absptr, because the ios linker can move functions inside object files to make thumb work,
+                        * so the offsets between two symbols in the text segment are not assembler constant.
+                        */
+                       g_assert (sizeof(gpointer) == 4);
+                       offset1 -= (gint32)(gsize)amodule->mono_eh_frame;
+                       offset2 -= (gint32)(gsize)amodule->mono_eh_frame;
+               }
 
                if (offset < offset1)
                        right = pos;
@@ -1982,13 +2061,22 @@ decode_llvm_mono_eh_frame (MonoAotModule *amodule, MonoDomain *domain,
                        break;
        }
 
-       code_start = amodule->mono_eh_frame + table [(pos * 2)];
-       /* This won't overflow because there is +1 entry in the table */
-       code_end = amodule->mono_eh_frame + table [(pos * 2) + 2];
+       if (func_encoding == DW_EH_PE_absptr) {
+               code_start = (gpointer)(gsize)table [(pos * 2)];
+               code_end = (gpointer)(gsize)table [(pos * 2) + 2];
+       } else {
+               code_start = amodule->mono_eh_frame + table [(pos * 2)];
+               /* This won't overflow because there is +1 entry in the table */
+               code_end = amodule->mono_eh_frame + table [(pos * 2) + 2];
+       }
        code_len = code_end - code_start;
 
        g_assert (code >= code_start && code < code_end);
 
+       if (amodule->thumb_end && (guint8*)code_start < amodule->thumb_end)
+               /* Clear thumb flag */
+               code_start = (guint8*)(((mgreg_t)code_start) & ~1);
+
        fde = amodule->mono_eh_frame + table [(pos * 2) + 1];   
        /* This won't overflow because there is +1 entry in the table */
        fde_len = table [(pos * 2) + 2 + 1] - table [(pos * 2) + 1];
@@ -2223,14 +2311,30 @@ decode_exception_debug_info (MonoAotModule *amodule, MonoDomain *domain,
                gi = mono_jit_info_get_generic_jit_info (jinfo);
                g_assert (gi);
 
-               if (from_llvm) {
-                       gi->has_this = this_reg != -1;
-                       gi->this_reg = this_reg;
-                       gi->this_offset = this_offset;
+               gi->nlocs = decode_value (p, &p);
+               if (gi->nlocs) {
+                       gi->locations = mono_domain_alloc0 (domain, gi->nlocs * sizeof (MonoDwarfLocListEntry));
+                       for (i = 0; i < gi->nlocs; ++i) {
+                               MonoDwarfLocListEntry *entry = &gi->locations [i];
+
+                               entry->is_reg = decode_value (p, &p);
+                               entry->reg = decode_value (p, &p);
+                               if (!entry->is_reg)
+                                       entry->offset = decode_value (p, &p);
+                               if (i > 0)
+                                       entry->from = decode_value (p, &p);
+                               entry->to = decode_value (p, &p);
+                       }
                } else {
-                       gi->has_this = decode_value (p, &p);
-                       gi->this_reg = decode_value (p, &p);
-                       gi->this_offset = decode_value (p, &p);
+                       if (from_llvm) {
+                               gi->has_this = this_reg != -1;
+                               gi->this_reg = this_reg;
+                               gi->this_offset = this_offset;
+                       } else {
+                               gi->has_this = decode_value (p, &p);
+                               gi->this_reg = decode_value (p, &p);
+                               gi->this_offset = decode_value (p, &p);
+                       }
                }
 
                /* This currently contains no data */
@@ -2415,7 +2519,7 @@ MonoJitInfo *
 mono_aot_find_jit_info (MonoDomain *domain, MonoImage *image, gpointer addr)
 {
        int pos, left, right, offset, offset1, offset2, code_len;
-       int method_index, table_len, is_wrapper;
+       int method_index, table_len;
        guint32 token;
        MonoAotModule *amodule = image->aot_module;
        MonoMethod *method;
@@ -2453,15 +2557,19 @@ mono_aot_find_jit_info (MonoDomain *domain, MonoImage *image, gpointer addr)
                for (i = 0; i < offsets_len -1; ++i)
                        g_assert (code_offsets [(i * 2)] <= code_offsets [(i + 1) * 2]);
 
+               amodule->sorted_code_offsets_len = offsets_len;
+               mono_memory_barrier ();
                if (InterlockedCompareExchangePointer ((gpointer*)&amodule->sorted_code_offsets, code_offsets, NULL) != NULL)
                        /* Somebody got in before us */
                        g_free (code_offsets);
-               amodule->sorted_code_offsets_len = offsets_len;
        }
 
        code_offsets = amodule->sorted_code_offsets;
        offsets_len = amodule->sorted_code_offsets_len;
 
+       if (offsets_len > 0 && (offset < code_offsets [0] || offset >= (amodule->code_end - amodule->code)))
+               return NULL;
+
        /* Binary search in the sorted_code_offsets table */
        left = 0;
        right = offsets_len;
@@ -2533,10 +2641,10 @@ mono_aot_find_jit_info (MonoDomain *domain, MonoImage *image, gpointer addr)
                        }
 
                        p = amodule->blob + table [(pos * 2) + 1];
-                       is_wrapper = decode_value (p, &p);
-                       g_assert (!is_wrapper);
                        method = decode_resolve_method_ref (amodule, p, &p);
-                       g_assert (method);
+                       if (!method)
+                               /* Happens when a random address is passed in which matches a not-yey called wrapper encoded using its name */
+                               return NULL;
                } else {
                        token = mono_metadata_make_token (MONO_TABLE_METHOD, method_index + 1);
                        method = mono_get_method (image, token, NULL);
@@ -2867,7 +2975,7 @@ load_method (MonoDomain *domain, MonoAotModule *amodule, MonoImage *image, MonoM
 
        info = &amodule->blob [mono_aot_get_offset (amodule->method_info_offsets, method_index)];
 
-       if (amodule->thumb_end && code < amodule->thumb_end) {
+       if (amodule->thumb_end && code < amodule->thumb_end && ((amodule->info.flags & MONO_AOT_FILE_FLAG_DIRECT_METHOD_ADDRESSES) == 0)) {
                /* Convert this into a thumb address */
                g_assert ((amodule->code_offsets [method_index] & 0x1) == 0);
                code = &amodule->code [amodule->code_offsets [method_index] + 1];
@@ -2955,6 +3063,9 @@ load_method (MonoDomain *domain, MonoAotModule *amodule, MonoImage *image, MonoM
                        mono_mempool_destroy (mp);
        }
 
+       if (mini_get_debug_options ()->load_aot_jit_info_eagerly)
+               jinfo = mono_aot_find_jit_info (domain, amodule->assembly->image, code);
+
        if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT)) {
                char *full_name;
 
@@ -3561,10 +3672,23 @@ mono_aot_get_plt_entry (guint8 *code)
        g_assert_not_reached ();
 #endif
 
+#ifdef MONOTOUCH
+       while (target != NULL) {
+               if ((target >= (guint8*)(amodule->plt)) && (target < (guint8*)(amodule->plt_end)))
+                       return target;
+               
+               // Add 4 since mono_arch_get_call_target assumes we're passing
+               // the instruction after the actual branch instruction.
+               target = mono_arch_get_call_target (target + 4);
+       }
+
+       return NULL;
+#else
        if ((target >= (guint8*)(amodule->plt)) && (target < (guint8*)(amodule->plt_end)))
                return target;
        else
                return NULL;
+#endif
 }
 
 /*
@@ -3777,9 +3901,16 @@ get_numerous_trampoline (MonoAotTrampoline tramp_type, int n_got_slots, MonoAotM
 
        *out_amodule = amodule;
 
-       if (amodule->trampoline_index [tramp_type] == amodule->info.num_trampolines [tramp_type])
-               g_error ("Ran out of trampolines of type %d in '%s' (%d)\n", tramp_type, image->name, amodule->info.num_trampolines [tramp_type]);
-
+       if (amodule->trampoline_index [tramp_type] == amodule->info.num_trampolines [tramp_type]) {
+               g_error ("Ran out of trampolines of type %d in '%s' (%d)%s\n", 
+                        tramp_type, image->name, amodule->info.num_trampolines [tramp_type],
+#ifdef MONOTOUCH
+                        ". See http://docs.xamarin.com/ios/troubleshooting for instruction on how to fix this condition"
+#else
+                        ""
+#endif
+                        );
+       }
        index = amodule->trampoline_index [tramp_type] ++;
 
        mono_aot_unlock ();