X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Frawbuffer.c;h=4475c1271a3b26160fb401c9d7385062273de307;hb=cd0f05d6fd528c0f63327a908e2be2f4a9620933;hp=9759b215387c47451be3ee5796383c08d3fc47e0;hpb=c7d7de6ab2001f82a5410183b27ff0c14a65f740;p=mono.git diff --git a/mono/metadata/rawbuffer.c b/mono/metadata/rawbuffer.c index 9759b215387..4475c1271a3 100644 --- a/mono/metadata/rawbuffer.c +++ b/mono/metadata/rawbuffer.c @@ -7,72 +7,336 @@ * (C) 2001 Ximian, Inc. */ #include +#if defined(PLATFORM_WIN32) +#define USE_WIN32_API 1 +#endif + #include +#include +#ifdef USE_WIN32_API +#include +#include +#include +#else #include +#endif #include #include #include "rawbuffer.h" -#define PAGESIZE 8192 +#include -GHashTable *malloc_map; +#define ROUND_DOWN(VALUE,SIZE) ((VALUE) & ~((SIZE) - 1)) +#define ROUND_UP(VALUE,SIZE) (ROUND_DOWN((VALUE) + (SIZE) - 1, (SIZE))) +#if SIZEOF_VOID_P == 8 +#define UINTPTR_TYPE guint64 +#else +#define UINTPTR_TYPE guint32 +#endif -void * -mono_raw_buffer_load (int fd, int is_writable, guint32 base, size_t size) +static GHashTable *mmap_map = NULL; +static size_t alignment = 0; +#define mono_mmap_lock() EnterCriticalSection (&mmap_mutex) +#define mono_mmap_unlock() LeaveCriticalSection (&mmap_mutex) +static CRITICAL_SECTION mmap_mutex; +static gboolean make_unreadable = FALSE; +static guint32 n_pagefaults = 0; + +static void +get_alignment (void) +{ +#ifdef USE_WIN32_API + SYSTEM_INFO info; + + GetSystemInfo (&info); + alignment = info.dwAllocationGranularity; +#else + alignment = getpagesize (); +#endif +} + +static void * +mono_raw_buffer_load_malloc (int fd, int is_writable, guint32 base, size_t size) { + void *ptr; + + ptr = g_malloc (size); + if (ptr == NULL) + return NULL; + + if (lseek (fd, base, 0) == (off_t) -1) { + g_free (ptr); + return NULL; + } + + read (fd, ptr, size); + return ptr; +} + +static void +mono_raw_buffer_free_malloc (void *base) +{ + g_free (base); +} + +void +mono_raw_buffer_init (void) +{ + InitializeCriticalSection (&mmap_mutex); + + get_alignment (); + + mmap_map = g_hash_table_new (NULL, NULL); +} + +void +mono_raw_buffer_cleanup (void) +{ + g_hash_table_destroy (mmap_map); + + DeleteCriticalSection (&mmap_mutex); +} + +static void * +mono_raw_buffer_load_mmap (int fd, int is_writable, guint32 base, size_t size) +{ +#ifdef USE_WIN32_API + /* FileMapping implementation */ + + DWORD start, end; + int prot, access; + void *ptr; + HANDLE file, mapping; + + start = ROUND_DOWN (base, alignment); + end = base + size; + + if (is_writable) { + prot = PAGE_WRITECOPY; + access = FILE_MAP_COPY; + } + else { + prot = PAGE_READONLY; + access = FILE_MAP_READ; + } + + file = (HANDLE) _get_osfhandle (fd); + mapping = CreateFileMapping (file, NULL, prot, 0, 0, NULL); + if (mapping == NULL) + return 0; + + ptr = MapViewOfFile (mapping, access, 0, start, end - start); + if (ptr == NULL) { + CloseHandle (mapping); + return 0; + } + + mono_mmap_lock (); + g_hash_table_insert (mmap_map, ptr, GINT_TO_POINTER (mapping)); + mono_mmap_unlock (); + + return ((char *)ptr) + (base - start); + +#else + /* mmap implementation */ + + size_t start, end; int prot = PROT_READ; int flags = 0; - void *ptr, *mmap_ptr; - + void *ptr; + + start = ROUND_DOWN (base, alignment); + end = ROUND_UP (base + size, alignment); + if (is_writable){ prot |= PROT_WRITE; + flags = MAP_SHARED; + } else { + flags = MAP_PRIVATE; } - flags = MAP_PRIVATE; - start = base & ~(PAGESIZE - 1); - end = (base + size + PAGESIZE - 1) & ~(PAGESIZE - 1); + ptr = mmap (0, end - start, prot, flags, fd, start); + + if (ptr == (void *) -1) + return 0; - /* - * Apparently on cygwin the mmpa succedes, but not all the - * area is mapped in and we get segfaults later. + /* + * This seems to prevent segmentation faults on Fedora Linux, no + * idea why :). See + * http://bugzilla.ximian.com/show_bug.cgi?id=49499 + * for more info. */ -#ifdef __CYGWIN__ - mmap_ptr = (void *) -1; + if (mprotect (ptr, end - start, prot | PROT_EXEC) != 0) + g_warning (G_GNUC_PRETTY_FUNCTION + ": mprotect failed: %s", g_strerror (errno)); + + if (make_unreadable) { + int res = mprotect (ptr, end - start, 0); + g_assert (res == 0); + } + + mono_mmap_lock (); + g_hash_table_insert (mmap_map, ptr, GINT_TO_POINTER (size)); + mono_mmap_unlock (); + + return ((char *)ptr) + (base - start); +#endif +} + +static void +mono_raw_buffer_free_mmap (void *base) +{ + int value; + + mono_mmap_lock (); + value = GPOINTER_TO_INT (g_hash_table_lookup (mmap_map, base)); + mono_mmap_unlock (); + +#ifdef USE_WIN32_API + UnmapViewOfFile (base); + CloseHandle ((HANDLE) value); #else - mmap_ptr = mmap (0, end - start, prot, flags, fd, start); + munmap (base, value); #endif - if (mmap_ptr == (void *) -1){ - ptr = g_malloc (size); - if (ptr == NULL) - return NULL; - if (lseek (fd, base, 0) == (off_t) -1) - return NULL; - read (fd, ptr, size); - return ptr; - } - if (malloc_map == NULL) - malloc_map = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +mono_raw_buffer_update_mmap (void *base, size_t size) +{ +#ifdef USE_WIN32_API + FlushViewOfFile (base, size); +#else + msync (base, size, MS_SYNC); +#endif +} + +void * +mono_raw_buffer_load (int fd, int is_writable, guint32 base, size_t size) +{ + void *ptr; + + ptr = mono_raw_buffer_load_mmap (fd, is_writable, base, size); + if (ptr == 0) + ptr = mono_raw_buffer_load_malloc (fd, is_writable, base, size); + + return ptr; +} + +void +mono_raw_buffer_update (void *buffer, size_t size) +{ + char *mmap_base; + gboolean exists; - g_hash_table_insert (malloc_map, mmap_ptr, GINT_TO_POINTER (size)); + mmap_base = (gpointer)(ROUND_DOWN ((UINTPTR_TYPE) (buffer), alignment)); - return ((char *)mmap_ptr) + (base - start); + mono_mmap_lock (); + exists = g_hash_table_lookup (mmap_map, mmap_base) != NULL; + mono_mmap_unlock (); + if (exists) + mono_raw_buffer_update_mmap (mmap_base, size); } void mono_raw_buffer_free (void *buffer) { - int size, diff; - char *base; - - if (!malloc_map){ - g_free (buffer); - return; - } + char *mmap_base; + gboolean exists; - diff = ((unsigned int) buffer) & (PAGESIZE - 1); - base = ((char *)buffer) - diff; + mmap_base = (gpointer)(ROUND_DOWN ((UINTPTR_TYPE) (buffer), alignment)); - size = GPOINTER_TO_INT (g_hash_table_lookup (malloc_map, base)); - munmap (base, size); + exists = g_hash_table_lookup (mmap_map, mmap_base) != NULL; + if (exists) + mono_raw_buffer_free_mmap (mmap_base); + else + mono_raw_buffer_free_malloc (buffer); +} + +/* + * mono_raw_buffer_set_make_unreadable: + * + * Set whenever to make all mmaped memory unreadable. In conjuction with a + * SIGSEGV handler, this is useful to find out which pages the runtime tries to read. + */ +void +mono_raw_buffer_set_make_unreadable (gboolean unreadable) +{ + make_unreadable = unreadable; +} + +typedef struct { + gboolean found; + void *ptr; +} FindMapUserData; + +static void +find_map (void *start, guint32 size, gpointer user_data) +{ + FindMapUserData *data = (FindMapUserData*)user_data; + + if (!data->found) + if (((guint8*)data->ptr >= (guint8*)start) && ((guint8*)data->ptr < (guint8*)start + size)) + data->found = TRUE; +} + +/* + * mono_raw_buffer_is_pagefault: + * + * Should be called from a SIGSEGV signal handler to find out whenever @ptr is + * within memory allocated by this module. + */ +gboolean +mono_raw_buffer_is_pagefault (void *ptr) +{ + FindMapUserData data; + + if (!make_unreadable) + return FALSE; + + data.found = FALSE; + data.ptr = ptr; + + mono_mmap_lock (); + g_hash_table_foreach (mmap_map, (GHFunc)find_map, &data); + mono_mmap_unlock (); + + return data.found; +} + +/* + * mono_raw_buffer_handle_pagefault: + * + * Handle a pagefault caused by an unreadable page by making it readable again. + */ +void +mono_raw_buffer_handle_pagefault (void *ptr) +{ +#ifndef PLATFORM_WIN32 + guint8* start = (guint8*)ROUND_DOWN (((gssize)ptr), alignment); + int res; + + mono_mmap_lock (); + res = mprotect (start, alignment, PROT_READ); + g_assert (res == 0); + + n_pagefaults ++; + mono_mmap_unlock (); +#endif +} + +/* + * mono_raw_buffer_get_n_pagefaults: + * + * Return the number of times handle_pagefault is called. + * To count the number of pagefaults caused by a block of code use code like this: + * + * int prev_pagefaults = mono_raw_buffer_get_n_pagefaults (); + * + * int new_pagefaults = mono_raw_buffer_get_n_pagefaults () - prev_pagefaults; + */ +guint32 +mono_raw_buffer_get_n_pagefaults (void) +{ + return n_pagefaults; }