This is a patch that attempts to override mmap, mmap64, and munmap so that 
host libs can used memory from the Wine heap instead of being restricted to 
the address space Wine leaves unreserved. It is based on Albert Lee's patch 
found here:

http://bugs.winehq.org/show_bug.cgi?id=13335#c170

It has been enhanced to fix the problem with using off64_t in the 
wine/library.h header by using ULONGLONG instead, and it should override the 
aforementioned functions with the correct prototype in 32-bit and 64-bit in a 
much simpler way.

It currently relies on the system having mmap or mmap64 that uses a 64-bit 
offset type, though if that's a problem, an extra wrapper can probably be 
made. It also relies on ULONGLONG being a 64-bit type, but from what I can 
tell, that will always true.

If there's a problem with it, or some way it can be improved, please let me 
know. :)
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index 212edf5..d36f560 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -69,6 +69,7 @@ extern HANDLE thread_init(void);
 extern void actctx_init(void);
 extern void virtual_init(void);
 extern void virtual_init_threading(void);
+extern void virtual_init_mmap(void);
 extern void fill_cpu_info(void);
 extern void heap_set_debug_flags( HANDLE handle );
 
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index df6c3e2..c7f70bf 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -297,6 +297,7 @@ HANDLE thread_init(void)
 
     fill_cpu_info();
 
+    virtual_init_mmap();
     return exe_file;
 }
 
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c
index a42ed5f..419f7ed 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -155,6 +155,11 @@ static void *preload_reserve_end;
 static int use_locks;
 static int force_exec_prot;  /* whether to force PROT_EXEC on all PROT_READ mmaps */
 
+static void *virtual_mmap_wrapper( void *addr, size_t size, int prot, int flags, int fd, ULONGLONG offset );
+static int virtual_munmap_wrapper( void *addr, size_t size );
+static void *(*sys_mmap)( void *addr, size_t size, int prot, int flags, int fd, ULONGLONG offset );
+static int (*sys_munmap)( void *addr, size_t size );
+
 
 /***********************************************************************
  *           VIRTUAL_GetProtStr
@@ -366,7 +371,7 @@ static void add_reserved_area( void *addr, size_t size )
     {
         /* unmap the part of the area that is below the limit */
         assert( (char *)addr + size > (char *)user_space_limit );
-        munmap( addr, (char *)user_space_limit - (char *)addr );
+        sys_munmap( addr, (char *)user_space_limit - (char *)addr );
         size -= (char *)user_space_limit - (char *)addr;
         addr = user_space_limit;
     }
@@ -394,11 +399,11 @@ static void remove_reserved_area( void *addr, size_t size )
     {
         if ((char *)view->base >= (char *)addr + size)
         {
-            munmap( addr, size );
+            sys_munmap( addr, size );
             break;
         }
         if ((char *)view->base + view->size <= (char *)addr) continue;
-        if (view->base > addr) munmap( addr, (char *)view->base - (char *)addr );
+        if (view->base > addr) sys_munmap( addr, (char *)view->base - (char *)addr );
         if ((char *)view->base + view->size > (char *)addr + size) break;
         size = (char *)addr + size - ((char *)view->base + view->size);
         addr = (char *)view->base + view->size;
@@ -423,14 +428,17 @@ static inline int is_beyond_limit( const void *addr, size_t size, const void *li
  * Unmap an area, or simply replace it by an empty mapping if it is
  * in a reserved area. The csVirtual section must be held by caller.
  */
-static inline void unmap_area( void *addr, size_t size )
+static inline int unmap_area( void *addr, size_t size )
 {
+    int ret = 0;
+
     if (wine_mmap_is_in_reserved_area( addr, size ))
         wine_anon_mmap( addr, size, PROT_NONE, MAP_NORESERVE | MAP_FIXED );
     else if (is_beyond_limit( addr, size, user_space_limit ))
         add_reserved_area( addr, size );
     else
-        munmap( addr, size );
+        ret = sys_munmap( addr, size );
+    return ret;
 }
 
 
@@ -700,12 +708,12 @@ static inline void *unmap_extra_space( void *ptr, size_t total_size, size_t want
     if ((ULONG_PTR)ptr & mask)
     {
         size_t extra = mask + 1 - ((ULONG_PTR)ptr & mask);
-        munmap( ptr, extra );
+        sys_munmap( ptr, extra );
         ptr = (char *)ptr + extra;
         total_size -= extra;
     }
     if (total_size > wanted_size)
-        munmap( (char *)ptr + wanted_size, total_size - wanted_size );
+        sys_munmap( (char *)ptr + wanted_size, total_size - wanted_size );
     return ptr;
 }
 
@@ -792,7 +800,7 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size,
             {
                 /* We couldn't get the address we wanted */
                 if (is_beyond_limit( ptr, size, user_space_limit )) add_reserved_area( ptr, size );
-                else munmap( ptr, size );
+                else sys_munmap( ptr, size );
                 return STATUS_CONFLICTING_ADDRESSES;
             }
             break;
@@ -847,6 +855,143 @@ done:
 
 
 /***********************************************************************
+ *           virtual_mmap_wrapper
+ */
+static void *virtual_mmap_wrapper( void *addr, size_t size, int prot, int flags, int fd, ULONGLONG offset )
+{
+    void *ptr;
+    struct alloc_area alloc;
+    sigset_t sigset;
+
+    if (!virtual_heap || (flags & MAP_FIXED))
+        return sys_mmap( addr, size, prot, flags, fd, offset );
+
+    size = ROUND_SIZE( addr, size );
+
+    server_enter_uninterrupted_section( &csVirtual, &sigset );
+
+    if (addr)
+    {
+        addr = ROUND_ADDR( addr, page_mask );
+        switch (wine_mmap_is_in_reserved_area( addr, size ))
+        {
+        case -1: /* partially in a reserved area */
+            break;
+        case 0:  /* not in a reserved area, do a normal allocation */
+            ptr = sys_mmap( addr, size, prot, flags, fd, offset );
+            if (ptr == (void *)-1 && errno == ENOMEM) break;
+            goto done;
+        default:
+        case 1:  /* in a reserved area, make sure the address is available */
+            if (!find_view_range( addr, size ))
+            {
+                /* replace the reserved area by our mapping */
+                ptr = sys_mmap( addr, size, prot, flags | MAP_FIXED, fd, offset );
+                goto done;
+            }
+            break;
+        }
+    }
+
+    alloc.size = size;
+    alloc.mask = page_mask;
+    alloc.top_down = 1;
+    alloc.limit = address_space_limit;
+    if (wine_mmap_enum_reserved_areas( alloc_reserved_area_callback, &alloc, 1 ))
+        ptr = sys_mmap( alloc.result, size, prot, flags | MAP_FIXED, fd, offset );
+    else
+        ptr = sys_mmap( NULL, size, prot, flags, fd, offset );
+
+done:
+    if (ptr != (void *)-1)
+    {
+        struct file_view *view;
+        WORD vprot = VPROT_COMMITTED | VPROT_SYSTEM | VPROT_NOEXEC;
+
+        if (prot != PROT_NONE)
+        {
+            if (prot & PROT_READ) vprot |= VPROT_READ;
+            if (prot & PROT_WRITE) vprot |= VPROT_WRITE;
+            if (prot & PROT_EXEC) vprot |= VPROT_EXEC;
+        }
+        if (create_view( &view, ptr, size, vprot ))
+        {
+            unmap_area( ptr, size );
+            ptr = (void *)-1;
+            errno = ENOMEM;
+            WARN( "Failed to create view for %p, %lu, 0x%04x\n",
+                  ptr, (ULONG_PTR)size, vprot );
+        }
+        else
+            TRACE( "%p %lu 0x%x 0x%x %d returning %p-%p\n",
+                  addr, (ULONG_PTR)size, prot, flags, fd, ptr, (char *)ptr + size );
+    }
+    server_leave_uninterrupted_section( &csVirtual, &sigset );
+    return ptr;
+}
+
+
+/***********************************************************************
+ *           virtual_munmap_wrapper
+ */
+static int virtual_munmap_wrapper( void *addr, size_t size )
+{
+    struct file_view *view, *next, *new_view;
+    sigset_t sigset;
+    char *end;
+    int ret;
+
+    size = ROUND_SIZE( addr, size );
+    addr = ROUND_ADDR( addr, page_mask );
+    end = (char *)addr + size;
+
+    TRACE( "%p-%p\n", addr, end );
+    server_enter_uninterrupted_section( &csVirtual, &sigset );
+
+    LIST_FOR_EACH_ENTRY_SAFE( view, next, &views_list, struct file_view, entry )
+    {
+        if ((char *)view->base >= end) break;
+        if ((char *)view->base + view->size <= (char *)addr) continue;
+
+        if (!(view->protect & VPROT_SYSTEM))
+            FIXME( "unmapping range %p-%p but view %p-%p is not system\n",
+                  addr, (char *)addr + size, view->base, (char *)view->base + size );
+
+        if ((char *)view->base >= (char *)addr)
+        {
+            if ((char *)view->base + view->size <= end)
+            {
+                /* view is entirely within the unmapped space, remove it */
+                TRACE( "deleting view %p-%p\n", view->base, (char *)view->base + view->size );
+                delete_view( view );
+                continue;
+            }
+            /* move the base to contain the remaining part of the view */
+            TRACE( "rebasing view %p-%p to %p\n", view->base, (char *)view->base + view->size, end );
+            view->base = end;
+            break;
+        }
+        if ((char *)view->base + view->size <= end)
+        {
+            /* truncate the view */
+            TRACE( "truncating view %p-%p to %p\n", view->base, (char *)view->base + view->size, addr );
+            view->size = (char *)addr - (char *)view->base;
+            continue;
+        }
+        /* create a new view for the remaining area */
+        TRACE( "creating new view %p-%p\n", end, (char *)view->base + view->size );
+        create_view( &new_view, end, (char *)view->base + view->size - end, view->protect );
+        view->size = (char *)addr - (char *)view->base;
+        break;
+    }
+
+    ret = unmap_area( addr, size );
+    server_leave_uninterrupted_section( &csVirtual, &sigset );
+    return ret;
+}
+
+
+/***********************************************************************
  *           map_file_into_view
  *
  * Wrapper for mmap() to map a file into a view, falling back to read if mmap fails.
@@ -867,7 +1012,7 @@ static NTSTATUS map_file_into_view( struct file_view *view, int fd, size_t start
     {
         int flags = MAP_FIXED | (shared_write ? MAP_SHARED : MAP_PRIVATE);
 
-        if (mmap( (char *)view->base + start, size, prot, flags, fd, offset ) != (void *)-1)
+        if (sys_mmap( (char *)view->base + start, size, prot, flags, fd, offset ) != (void *)-1)
             goto done;
 
         /* mmap() failed; if this is because the file offset is not    */
@@ -981,7 +1126,7 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot
         addr = wine_anon_mmap( low_64k, dosmem_size - 0x10000, unix_prot, 0 );
         if (addr != low_64k)
         {
-            if (addr != (void *)-1) munmap( addr, dosmem_size - 0x10000 );
+            if (addr != (void *)-1) sys_munmap( addr, dosmem_size - 0x10000 );
             return map_view( view, NULL, dosmem_size, 0xffff, 0, vprot );
         }
     }
@@ -998,7 +1143,7 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot
         }
         else
         {
-            if (addr != (void *)-1) munmap( addr, 0x10000 - page_size );
+            if (addr != (void *)-1) sys_munmap( addr, 0x10000 - page_size );
             addr = low_64k;
             TRACE( "failed to map low 64K range\n" );
         }
@@ -1350,6 +1495,11 @@ void virtual_init(void)
         }
     }
 
+    assert(wine_mmap != NULL);
+    assert(wine_munmap != NULL);
+    sys_mmap = wine_mmap;
+    sys_munmap = wine_munmap;
+
     /* try to find space in a reserved area for the virtual heap */
     if (!wine_mmap_enum_reserved_areas( alloc_virtual_heap, &heap_base, 1 ))
         heap_base = wine_anon_mmap( NULL, VIRTUAL_HEAP_SIZE, PROT_READ|PROT_WRITE, 0 );
@@ -1375,6 +1525,16 @@ void virtual_init_threading(void)
 
 
 /***********************************************************************
+ *           virtual_init_mmap
+ */
+void virtual_init_mmap(void)
+{
+    wine_mmap = virtual_mmap_wrapper;
+    wine_munmap = virtual_munmap_wrapper;
+}
+
+
+/***********************************************************************
  *           virtual_get_system_info
  */
 void virtual_get_system_info( SYSTEM_BASIC_INFORMATION *info )
diff --git a/include/wine/library.h b/include/wine/library.h
index 242bb69..7e0ce36 100644
--- a/include/wine/library.h
+++ b/include/wine/library.h
@@ -75,6 +75,8 @@ extern int wine_call_on_stack( int (*func)(void *), void *arg, void *stack );
 
 /* memory mappings */
 
+extern void *(*wine_mmap)( void *addr, size_t size, int prot, int flags, int fd, ULONGLONG offset );
+extern int (*wine_munmap)( void *addr, size_t size );
 extern void *wine_anon_mmap( void *start, size_t size, int prot, int flags );
 extern void wine_mmap_add_reserved_area( void *addr, size_t size );
 extern void wine_mmap_remove_reserved_area( void *addr, size_t size, int unmap );
diff --git a/libs/wine/mmap.c b/libs/wine/mmap.c
index b400189..799a55b 100644
--- a/libs/wine/mmap.c
+++ b/libs/wine/mmap.c
@@ -43,6 +43,9 @@
 
 #ifdef HAVE_MMAP
 
+void *(*wine_mmap)( void *addr, size_t size, int prot, int flags, int fd, ULONGLONG offset );
+int (*wine_munmap)( void *addr, size_t size );
+
 struct reserved_area
 {
     struct list entry;
@@ -140,12 +143,12 @@ static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
 
         /* Perform the mapping with MAP_FIXED set.  This is safe
            now, as none of the pages is currently in use. */
-        result = mmap( addr, len, prot, flags | MAP_FIXED, fildes, off );
+        result = wine_mmap( addr, len, prot, flags | MAP_FIXED, fildes, off );
         if ( result == addr )
             _exit(0);
 
         if ( result != (void *) -1 ) /* This should never happen ... */
-            munmap( result, len );
+            wine_munmap( result, len );
 
        _exit(1);
     }
@@ -174,7 +177,7 @@ static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
 
     if (!vm_allocate(mach_task_self(),&result,len,0))
     {
-        if (mmap( (void *)result, len, prot, flags | MAP_FIXED, fildes, off ) != MAP_FAILED)
+        if (wine_mmap( (void *)result, len, prot, flags | MAP_FIXED, fildes, off ) != MAP_FAILED)
             return 1;
         vm_deallocate(mach_task_self(),result,len);
     }
@@ -213,7 +216,7 @@ void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
             return start;
 #endif
     }
-    return mmap( start, size, prot, flags, get_fdzero(), 0 );
+    return wine_mmap( start, size, prot, flags, get_fdzero(), 0 );
 }
 
 
@@ -232,8 +235,8 @@ static inline int mmap_reserve( void *addr, size_t size )
 #elif defined(__APPLE__)
     return try_mmap_fixed( addr, size, PROT_NONE, flags, get_fdzero(), 0 );
 #endif
-    ptr = mmap( addr, size, PROT_NONE, flags, get_fdzero(), 0 );
-    if (ptr != addr && ptr != (void *)-1)  munmap( ptr, size );
+    ptr = wine_mmap( addr, size, PROT_NONE, flags, get_fdzero(), 0 );
+    if (ptr != addr && ptr != (void *)-1) wine_munmap( ptr, size );
     return (ptr == addr);
 }
 
@@ -260,8 +263,9 @@ static void reserve_area( void *addr, void *end )
             if (mincore( (caddr_t)addr + i, pagesize, &vec ) != -1) break;
 
         i &= ~granularity_mask;
-        if (i && mmap( addr, i, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
-                       get_fdzero(), 0 ) != (void *)-1)
+        if (i && wine_mmap( addr, i, PROT_NONE,
+                            MAP_FIXED | MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
+                            get_fdzero(), 0 ) != (void *)-1)
             wine_mmap_add_reserved_area( addr, i );
 
         i += granularity_mask + 1;
@@ -327,7 +331,7 @@ static void reserve_dos_area(void)
     ptr = wine_anon_mmap( (void *)page_size, dos_area_size - page_size, PROT_NONE, MAP_NORESERVE );
     if (ptr != (void *)page_size)
     {
-        if (ptr != (void *)-1) munmap( ptr, dos_area_size - page_size );
+        if (ptr != (void *)-1) wine_munmap( ptr, dos_area_size - page_size );
         return;
     }
     /* now add first page with MAP_FIXED */
@@ -485,7 +489,7 @@ void wine_mmap_remove_reserved_area( void *addr, size_t size, int unmap )
                 if ((char *)area->base + area->size > (char *)addr + size)
                 {
                     /* range overlaps beginning of area only -> shrink area */
-                    if (unmap) munmap( area->base, (char *)addr + size - (char *)area->base );
+                    if (unmap) wine_munmap( area->base, (char *)addr + size - (char *)area->base );
                     area->size -= (char *)addr + size - (char *)area->base;
                     area->base = (char *)addr + size;
                     break;
@@ -494,7 +498,7 @@ void wine_mmap_remove_reserved_area( void *addr, size_t size, int unmap )
                 {
                     /* range contains the whole area -> remove area completely */
                     ptr = list_next( &reserved_areas, ptr );
-                    if (unmap) munmap( area->base, area->size );
+                    if (unmap) wine_munmap( area->base, area->size );
                     list_remove( &area->entry );
                     free( area );
                     continue;
@@ -514,13 +518,13 @@ void wine_mmap_remove_reserved_area( void *addr, size_t size, int unmap )
                     }
                     else size = (char *)area->base + area->size - (char *)addr;
                     area->size = (char *)addr - (char *)area->base;
-                    if (unmap) munmap( addr, size );
+                    if (unmap) wine_munmap( addr, size );
                     break;
                 }
                 else
                 {
                     /* range overlaps end of area only -> shrink area */
-                    if (unmap) munmap( addr, (char *)area->base + area->size - (char *)addr );
+                    if (unmap) wine_munmap( addr, (char *)area->base + area->size - (char *)addr );
                     area->size = (char *)addr - (char *)area->base;
                 }
             }
diff --git a/libs/wine/wine.map b/libs/wine/wine.map
index 2159fac..3cf94c1 100644
--- a/libs/wine/wine.map
+++ b/libs/wine/wine.map
@@ -105,10 +105,12 @@ WINE_1.0
     wine_ldt_is_system;
     wine_ldt_realloc_entries;
     wine_ldt_set_entry;
+    wine_mmap;
     wine_mmap_add_reserved_area;
     wine_mmap_enum_reserved_areas;
     wine_mmap_is_in_reserved_area;
     wine_mmap_remove_reserved_area;
+    wine_munmap;
     wine_pthread_get_functions;
     wine_pthread_set_functions;
     wine_set_fs;
diff --git a/loader/main.c b/loader/main.c
index 628a0fa..a57d435 100644
--- a/loader/main.c
+++ b/loader/main.c
@@ -24,9 +24,6 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#ifdef HAVE_SYS_MMAN_H
-# include <sys/mman.h>
-#endif
 #ifdef HAVE_SYS_RESOURCE_H
 # include <sys/resource.h>
 #endif
@@ -136,7 +133,7 @@ static void check_vmsplit( void *stack )
     if (stack < (void *)0x80000000)
     {
         /* if the stack is below 0x80000000, assume we can safely try a munmap there */
-        if (munmap( (void *)0x80000000, 1 ) == -1 && errno == EINVAL)
+        if (wine_munmap( (void *)0x80000000, 1 ) == -1 && errno == EINVAL)
             fprintf( stderr,
                      "Warning: memory above 0x80000000 doesn't seem to be accessible.\n"
                      "Wine requires a 3G/1G user/kernel memory split to work properly.\n" );
@@ -186,6 +183,60 @@ static int pre_exec(void)
 #endif
 
 
+/***********************************************************************
+ * overrides for mmap, mmap64, munmap
+ */
+/* Because off_t can change size depending on _FILE_OFFSET_BITS=64 being set,
+ * we can't rely on it here. In most cases, the default off_t type follows the
+ * same pattern as unsigned long (4 bytes on 32-bit, 8 on 64).
+ * On FreeBSD, however, off_t is always a 64-bit type.
+ */
+#if defined(__FreeBSD__)
+typedef off_t offset_t;
+#else
+typedef unsigned long offset_t;
+#endif
+
+void *mmap( void *addr, size_t size, int prot, int flags, int fd, offset_t offset )
+{
+    return wine_mmap( addr, size, prot, flags, fd, offset );
+}
+
+void *mmap64( void *addr, size_t size, int prot, int flags, int fd, off64_t offset )
+{
+    return wine_mmap( addr, size, prot, flags, fd, offset );
+}
+
+int munmap( void *addr, size_t size )
+{
+    return wine_munmap( addr, size );
+}
+
+
+/**********************************************************************
+ *           init_mmap_functions
+ */
+static void init_mmap_functions(void)
+{
+    if (sizeof(offset_t) == sizeof(ULONGLONG))
+        wine_mmap = wine_dlsym( RTLD_NEXT, "mmap", NULL, 0 );
+    else
+        wine_mmap = wine_dlsym( RTLD_NEXT, "mmap64", NULL, 0 );
+    wine_munmap = wine_dlsym( RTLD_NEXT, "munmap", NULL, 0 );
+
+    if (wine_mmap == NULL)
+    {
+        fprintf( stderr, "Could not find mmap function\n" );
+        exit(1);
+    }
+    if (wine_munmap == NULL)
+    {
+        fprintf( stderr, "Could not find munmap function\n" );
+        exit(1);
+    }
+}
+
+
 /**********************************************************************
  *           main
  */
@@ -194,6 +245,8 @@ int main( int argc, char *argv[] )
     char error[1024];
     int i;
 
+    init_mmap_functions();
+
     if (!getenv( "WINELOADERNOEXEC" ))  /* first time around */
     {
         static char noexec[] = "WINELOADERNOEXEC=1";


Reply via email to