NaCl runtime fixes
[mono.git] / mono / utils / mono-codeman.c
index 6ce7b9844b873b90099e2ba9607d4226c74ce792..dcee2a99b3e28e16ecea4ebddc4de6e968b151e8 100644 (file)
 
 #include "mono-codeman.h"
 #include "mono-mmap.h"
+#include "mono-counters.h"
 #include "dlmalloc.h"
 #include <mono/metadata/class-internals.h>
+#include <mono/metadata/profiler-private.h>
 #ifdef HAVE_VALGRIND_MEMCHECK_H
 #include <valgrind/memcheck.h>
 #endif
+
+#if defined(__native_client_codegen__) && defined(__native_client__)
+#include <malloc.h>
+#include <nacl/nacl_dyncode.h>
+#endif
+
+static uintptr_t code_memory_used = 0;
+
 /*
  * AMD64 processors maintain icache coherency only for pages which are 
  * marked executable. Also, windows DEP requires us to obtain executable memory from
 #else
 #define MIN_ALIGN 8
 #endif
+#ifdef __native_client_codegen__
+/* For Google Native Client, all targets of indirect control flow need to    */
+/* be aligned to a 32-byte boundary. MIN_ALIGN was updated to 32 to force    */
+/* alignment for calls from tramp-x86.c to mono_global_codeman_reserve()     */
+/* and mono_domain_code_reserve().                                           */
+#undef MIN_ALIGN
+#define MIN_ALIGN 32
+#endif
 
 /* if a chunk has less than this amount of free space it's considered full */
 #define MAX_WASTAGE 32
@@ -73,10 +90,119 @@ struct _MonoCodeManager {
        int read_only;
        CodeChunk *current;
        CodeChunk *full;
+#if defined(__native_client_codegen__) && defined(__native_client__)
+       GHashTable *hash;
+#endif
 };
 
 #define ALIGN_INT(val,alignment) (((val) + (alignment - 1)) & ~(alignment - 1))
 
+#if defined(__native_client_codegen__) && defined(__native_client__)
+/* End of text segment, set by linker. 
+ * Dynamic text starts on the next allocated page.
+ */
+extern char etext[];
+char *next_dynamic_code_addr = NULL;
+
+/*
+ * This routine gets the next available bundle aligned
+ * pointer in the dynamic code section.  It does not check
+ * for the section end, this error will be caught in the
+ * service runtime.
+ */
+void*
+allocate_code(intptr_t increment)
+{
+       char *addr;
+       if (increment < 0) return NULL;
+       increment = increment & kNaClBundleMask ? (increment & ~kNaClBundleMask) + kNaClBundleSize : increment;
+       addr = next_dynamic_code_addr;
+       next_dynamic_code_addr += increment;
+       return addr;
+}
+
+int
+nacl_is_code_address (void *target)
+{
+       return (char *)target < next_dynamic_code_addr;
+}
+
+const int kMaxPatchDepth = 32;
+__thread unsigned char **patch_source_base = NULL;
+__thread unsigned char **patch_dest_base = NULL;
+__thread int *patch_alloc_size = NULL;
+__thread int patch_current_depth = -1;
+__thread int allow_target_modification = 1;
+
+void
+nacl_allow_target_modification (int val)
+{
+       allow_target_modification = val;
+}
+
+static void
+nacl_jit_check_init ()
+{
+       if (patch_source_base == NULL) {
+               patch_source_base = g_malloc (kMaxPatchDepth * sizeof(unsigned char *));
+               patch_dest_base = g_malloc (kMaxPatchDepth * sizeof(unsigned char *));
+               patch_alloc_size = g_malloc (kMaxPatchDepth * sizeof(int));
+       }
+}
+
+
+/* Given a patch target, modify the target such that patching will work when
+ * the code is copied to the data section.
+ */
+void*
+nacl_modify_patch_target (unsigned char *target)
+{
+       /* This seems like a bit of an ugly way to do this but the advantage
+        * is we don't have to worry about all the conditions in
+        * mono_resolve_patch_target, and it can be used by all the bare uses
+        * of <arch>_patch.
+        */
+       unsigned char *sb;
+       unsigned char *db;
+
+       if (!allow_target_modification) return target;
+
+       nacl_jit_check_init ();
+       sb = patch_source_base[patch_current_depth];
+       db = patch_dest_base[patch_current_depth];
+
+       if (target >= sb && (target < sb + patch_alloc_size[patch_current_depth])) {
+               /* Do nothing.  target is in the section being generated.
+                * no need to modify, the disp will be the same either way.
+                */
+       } else {
+               int target_offset = target - db;
+               target = sb + target_offset;
+       }
+       return target;
+}
+
+void*
+nacl_inverse_modify_patch_target (unsigned char *target)
+{
+       unsigned char *sb;
+       unsigned char *db;
+       int target_offset;
+
+       if (!allow_target_modification) return target;
+
+       nacl_jit_check_init ();
+       sb = patch_source_base[patch_current_depth];
+       db = patch_dest_base[patch_current_depth];
+
+       target_offset = target - sb;
+       target = db + target_offset;
+       return target;
+}
+
+
+#endif /* __native_client_codegen && __native_client__ */
+
 /**
  * mono_code_manager_new:
  *
@@ -98,6 +224,26 @@ mono_code_manager_new (void)
        cman->full = NULL;
        cman->dynamic = 0;
        cman->read_only = 0;
+#if defined(__native_client_codegen__) && defined(__native_client__)
+       if (next_dynamic_code_addr == NULL) {
+               const guint kPageMask = 0xFFFF; /* 64K pages */
+               next_dynamic_code_addr = (uintptr_t)(etext + kPageMask) & ~kPageMask;
+#if defined (__GLIBC__)
+               /* TODO: For now, just jump 64MB ahead to avoid dynamic libraries. */
+               next_dynamic_code_addr += (uintptr_t)0x4000000;
+#else
+               /* Workaround bug in service runtime, unable to allocate */
+               /* from the first page in the dynamic code section.    */
+               next_dynamic_code_addr += (uintptr_t)0x10000;
+#endif
+       }
+       cman->hash =  g_hash_table_new (NULL, NULL);
+       if (patch_source_base == NULL) {
+               patch_source_base = g_malloc (kMaxPatchDepth * sizeof(unsigned char *));
+               patch_dest_base = g_malloc (kMaxPatchDepth * sizeof(unsigned char *));
+               patch_alloc_size = g_malloc (kMaxPatchDepth * sizeof(int));
+       }
+#endif
        return cman;
 }
 
@@ -135,6 +281,7 @@ free_chunklist (CodeChunk *chunk)
 
        for (; chunk; ) {
                dead = chunk;
+               mono_profiler_code_chunk_destroy ((gpointer) dead->data);
                chunk = chunk->next;
                if (dead->flags == CODE_FLAG_MMAP) {
                        mono_vfree (dead->data, dead->size);
@@ -142,6 +289,7 @@ free_chunklist (CodeChunk *chunk)
                } else if (dead->flags == CODE_FLAG_MALLOC) {
                        dlfree (dead->data);
                }
+               code_memory_used -= dead->size;
                free (dead);
        }
 }
@@ -278,7 +426,10 @@ new_codechunk (int dynamic, int size)
                if (!ptr)
                        return NULL;
        } else {
-               ptr = mono_valloc (NULL, chunk_size, MONO_PROT_RWX | ARCH_MAP_FLAGS);
+               /* Allocate MIN_ALIGN-1 more than we need so we can still */
+               /* guarantee MIN_ALIGN alignment for individual allocs    */
+               /* from mono_code_manager_reserve_align.                  */
+               ptr = mono_valloc (NULL, chunk_size + MIN_ALIGN - 1, MONO_PROT_RWX | ARCH_MAP_FLAGS);
                if (!ptr)
                        return NULL;
        }
@@ -304,7 +455,10 @@ new_codechunk (int dynamic, int size)
        chunk->flags = flags;
        chunk->pos = bsize;
        chunk->bsize = bsize;
+       mono_profiler_code_chunk_new((gpointer) chunk->data, chunk->size);
 
+       code_memory_used += chunk_size;
+       mono_runtime_resource_check_limit (MONO_RESOURCE_JIT_CODE, code_memory_used);
        /*printf ("code chunk at: %p\n", ptr);*/
        return chunk;
 }
@@ -322,8 +476,10 @@ new_codechunk (int dynamic, int size)
 void*
 mono_code_manager_reserve_align (MonoCodeManager *cman, int size, int alignment)
 {
+#if !defined(__native_client__) || !defined(__native_client_codegen__)
        CodeChunk *chunk, *prev;
        void *ptr;
+       guint32 align_mask = alignment - 1;
 
        g_assert (!cman->read_only);
 
@@ -346,8 +502,10 @@ mono_code_manager_reserve_align (MonoCodeManager *cman, int size, int alignment)
        for (chunk = cman->current; chunk; chunk = chunk->next) {
                if (ALIGN_INT (chunk->pos, alignment) + size <= chunk->size) {
                        chunk->pos = ALIGN_INT (chunk->pos, alignment);
-                       ptr = chunk->data + chunk->pos;
-                       chunk->pos += size;
+                       /* Align the chunk->data we add to chunk->pos */
+                       /* or we can't guarantee proper alignment     */
+                       ptr = (void*)((((uintptr_t)chunk->data + align_mask) & ~(uintptr_t)align_mask) + chunk->pos);
+                       chunk->pos = ((char*)ptr - chunk->data) + size;
                        return ptr;
                }
        }
@@ -374,9 +532,33 @@ mono_code_manager_reserve_align (MonoCodeManager *cman, int size, int alignment)
        chunk->next = cman->current;
        cman->current = chunk;
        chunk->pos = ALIGN_INT (chunk->pos, alignment);
-       ptr = chunk->data + chunk->pos;
-       chunk->pos += size;
+       /* Align the chunk->data we add to chunk->pos */
+       /* or we can't guarantee proper alignment     */
+       ptr = (void*)((((uintptr_t)chunk->data + align_mask) & ~(uintptr_t)align_mask) + chunk->pos);
+       chunk->pos = ((char*)ptr - chunk->data) + size;
        return ptr;
+#else
+       unsigned char *temp_ptr, *code_ptr;
+       /* Round up size to next bundle */
+       alignment = kNaClBundleSize;
+       size = (size + kNaClBundleSize) & (~kNaClBundleMask);
+       /* Allocate a temp buffer */
+       temp_ptr = memalign (alignment, size);
+       g_assert (((uintptr_t)temp_ptr & kNaClBundleMask) == 0);
+       /* Allocate code space from the service runtime */
+       code_ptr = allocate_code (size);
+       /* Insert pointer to code space in hash, keyed by buffer ptr */
+       g_hash_table_insert (cman->hash, temp_ptr, code_ptr);
+
+       nacl_jit_check_init ();
+
+       patch_current_depth++;
+       patch_source_base[patch_current_depth] = temp_ptr;
+       patch_dest_base[patch_current_depth] = code_ptr;
+       patch_alloc_size[patch_current_depth] = size;
+       g_assert (patch_current_depth < kMaxPatchDepth);
+       return temp_ptr;
+#endif
 }
 
 /**
@@ -408,13 +590,51 @@ mono_code_manager_reserve (MonoCodeManager *cman, int size)
 void
 mono_code_manager_commit (MonoCodeManager *cman, void *data, int size, int newsize)
 {
+#if !defined(__native_client__) || !defined(__native_client_codegen__)
        g_assert (newsize <= size);
 
        if (cman->current && (size != newsize) && (data == cman->current->data + cman->current->pos - size)) {
                cman->current->pos -= size - newsize;
        }
+#else
+       unsigned char *code;
+       int status;
+       g_assert (newsize <= size);
+       code = g_hash_table_lookup (cman->hash, data);
+       g_assert (code != NULL);
+       /* Pad space after code with HLTs */
+       /* TODO: this is x86/amd64 specific */
+       while (newsize & kNaClBundleMask) {
+               *((char *)data + newsize) = 0xf4;
+               newsize++;
+       }
+       status = nacl_dyncode_create (code, data, newsize);
+       if (status != 0) {
+               unsigned char *codep;
+               fprintf(stderr, "Error creating Native Client dynamic code section attempted to be\n"
+                               "emitted at %p (hex dissasembly of code follows):\n", code);
+               for (codep = data; codep < data + newsize; codep++)
+                       fprintf(stderr, "%02x ", *codep);
+               fprintf(stderr, "\n");
+               g_assert_not_reached ();
+       }
+       g_hash_table_remove (cman->hash, data);
+       g_assert (data == patch_source_base[patch_current_depth]);
+       g_assert (code == patch_dest_base[patch_current_depth]);
+       patch_current_depth--;
+       g_assert (patch_current_depth >= -1);
+       free (data);
+#endif
 }
 
+#if defined(__native_client_codegen__) && defined(__native_client__)
+void *
+nacl_code_manager_get_code_dest (MonoCodeManager *cman, void *data)
+{
+       return g_hash_table_lookup (cman->hash, data);
+}
+#endif
+
 /**
  * mono_code_manager_size:
  * @cman: a code manager