2 * Wine memory mappings support
4 * Copyright 2000, 2004 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "wine/port.h"
29 #include <sys/types.h>
30 #ifdef HAVE_SYS_MMAN_H
40 #include "wine/library.h"
41 #include "wine/list.h"
50 static struct list reserved_areas
= LIST_INIT(reserved_areas
);
51 static const int granularity_mask
= 0xffff; /* reserved areas have 64k granularity */
54 #define MAP_NORESERVE 0
58 static inline int munmap( void *ptr
, size_t size
) { return 0; }
62 #if (defined(__svr4__) || defined(__NetBSD__)) && !defined(MAP_TRYFIXED)
63 /***********************************************************************
66 * The purpose of this routine is to emulate the behaviour of
67 * the Linux mmap() routine if a non-NULL address is passed,
68 * but the MAP_FIXED flag is not set. Linux in this case tries
69 * to place the mapping at the specified address, *unless* the
70 * range is already in use. Solaris, however, completely ignores
71 * the address argument in this case.
73 * As Wine code occasionally relies on the Linux behaviour, e.g. to
74 * be able to map non-relocateable PE executables to their proper
75 * start addresses, or to map the DOS memory to 0, this routine
76 * emulates the Linux behaviour by checking whether the desired
77 * address range is still available, and placing the mapping there
78 * using MAP_FIXED if so.
80 static int try_mmap_fixed (void *addr
, size_t len
, int prot
, int flags
,
81 int fildes
, off_t off
)
83 char * volatile result
= NULL
;
84 int pagesize
= getpagesize();
87 /* We only try to map to a fixed address if
88 addr is non-NULL and properly aligned,
89 and MAP_FIXED isn't already specified. */
93 if ( (uintptr_t)addr
& (pagesize
-1) )
95 if ( flags
& MAP_FIXED
)
98 /* We use vfork() to freeze all threads of the
99 current process. This allows us to check without
100 race condition whether the desired memory range is
101 already in use. Note that because vfork() shares
102 the address spaces between parent and child, we
103 can actually perform the mapping in the child. */
105 if ( (pid
= vfork()) == -1 )
107 perror("try_mmap_fixed: vfork");
115 /* We call mincore() for every page in the desired range.
116 If any of these calls succeeds, the page is already
117 mapped and we must fail. */
118 for ( i
= 0; i
< len
; i
+= pagesize
)
119 if ( mincore( (caddr_t
)addr
+ i
, pagesize
, &vec
) != -1 )
122 /* Perform the mapping with MAP_FIXED set. This is safe
123 now, as none of the pages is currently in use. */
124 result
= mmap( addr
, len
, prot
, flags
| MAP_FIXED
, fildes
, off
);
125 if ( result
== addr
)
128 if ( result
!= (void *) -1 ) /* This should never happen ... */
129 munmap( result
, len
);
134 /* vfork() lets the parent continue only after the child
135 has exited. Furthermore, Wine sets SIGCHLD to SIG_IGN,
136 so we don't need to wait for the child. */
138 return result
== addr
;
141 #elif defined(__APPLE__)
143 #include <mach/mach_init.h>
144 #include <mach/vm_map.h>
147 * On Darwin, we can use the Mach call vm_allocate to allocate
148 * anonymous memory at the specified address, and then use mmap with
149 * MAP_FIXED to replace the mapping.
151 static int try_mmap_fixed (void *addr
, size_t len
, int prot
, int flags
,
152 int fildes
, off_t off
)
154 vm_address_t result
= (vm_address_t
)addr
;
156 if (!vm_allocate(mach_task_self(),&result
,len
,0))
158 if (mmap( (void *)result
, len
, prot
, flags
| MAP_FIXED
, fildes
, off
) != MAP_FAILED
)
160 vm_deallocate(mach_task_self(),result
,len
);
165 #endif /* (__svr4__ || __NetBSD__) && !MAP_TRYFIXED */
168 /***********************************************************************
171 * Portable wrapper for anonymous mmaps
173 void *wine_anon_mmap( void *start
, size_t size
, int prot
, int flags
)
176 static int fdzero
= -1;
183 if ((fdzero
= open( "/dev/zero", O_RDONLY
)) == -1)
185 perror( "/dev/zero: open" );
189 #endif /* MAP_ANON */
192 flags
&= ~MAP_SHARED
;
195 /* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
197 flags
|= MAP_PRIVATE
;
200 if (!(flags
& MAP_FIXED
))
202 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
203 /* Even FreeBSD 5.3 does not properly support NULL here. */
204 if( start
== NULL
) start
= (void *)0x110000;
208 /* If available, this will attempt a fixed mapping in-kernel */
209 flags
|= MAP_TRYFIXED
;
210 #elif defined(__svr4__) || defined(__NetBSD__) || defined(__APPLE__)
211 if ( try_mmap_fixed( start
, size
, prot
, flags
, fdzero
, 0 ) )
215 return mmap( start
, size
, prot
, flags
, fdzero
, 0 );
224 /***********************************************************************
227 * Reserve as much memory as possible in the given area.
228 * FIXME: probably needs a different algorithm for Solaris
230 static void reserve_area( void *addr
, void *end
)
233 size_t size
= (char *)end
- (char *)addr
;
237 if ((ptr
= wine_anon_mmap( addr
, size
, PROT_NONE
, MAP_NORESERVE
)) != (void *)-1)
241 wine_mmap_add_reserved_area( addr
, size
);
244 else munmap( ptr
, size
);
246 if (size
> granularity_mask
+ 1)
248 size_t new_size
= (size
/ 2) & ~granularity_mask
;
249 reserve_area( addr
, (char *)addr
+ new_size
);
250 reserve_area( (char *)addr
+ new_size
, end
);
255 /***********************************************************************
258 * Reserve the DOS area (0x00000000-0x00110000).
260 static void reserve_dos_area(void)
262 const size_t page_size
= getpagesize();
263 const size_t dos_area_size
= 0x110000;
266 /* first page has to be handled specially */
267 ptr
= wine_anon_mmap( (void *)page_size
, dos_area_size
- page_size
, PROT_NONE
, MAP_NORESERVE
);
268 if (ptr
!= (void *)page_size
)
270 if (ptr
!= (void *)-1) munmap( ptr
, dos_area_size
- page_size
);
273 /* now add first page with MAP_FIXED */
274 wine_anon_mmap( NULL
, page_size
, PROT_NONE
, MAP_NORESERVE
|MAP_FIXED
);
275 wine_mmap_add_reserved_area( NULL
, dos_area_size
);
279 /***********************************************************************
284 struct reserved_area
*area
;
286 #if defined(__i386__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) /* commented out until FreeBSD gets fixed */
288 char * const stack_ptr
= &stack
;
289 char *user_space_limit
= (char *)0x80000000;
291 /* check for a reserved area starting at the user space limit */
292 /* to avoid wasting time trying to allocate it again */
293 LIST_FOR_EACH( ptr
, &reserved_areas
)
295 area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
296 if ((char *)area
->base
> user_space_limit
) break;
297 if ((char *)area
->base
+ area
->size
> user_space_limit
)
299 user_space_limit
= (char *)area
->base
+ area
->size
;
304 if (stack_ptr
>= user_space_limit
)
306 char *base
= stack_ptr
- ((unsigned int)stack_ptr
& granularity_mask
) - (granularity_mask
+ 1);
307 if (base
> user_space_limit
) reserve_area( user_space_limit
, base
);
308 base
= stack_ptr
- ((unsigned int)stack_ptr
& granularity_mask
) + (granularity_mask
+ 1);
310 /* Linux heuristic: if the stack top is at c0000000, assume the address space */
311 /* ends there, this avoids a lot of futile allocation attempts */
312 if (base
!= (char *)0xc0000000)
314 reserve_area( base
, 0 );
316 else reserve_area( user_space_limit
, 0 );
317 #endif /* __i386__ */
319 /* reserve the DOS area if not already done */
321 ptr
= list_head( &reserved_areas
);
324 area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
325 if (!area
->base
) return; /* already reserved */
330 #else /* HAVE_MMAP */
338 /***********************************************************************
339 * wine_mmap_add_reserved_area
341 * Add an address range to the list of reserved areas.
342 * Caller must have made sure the range is not used by anything else.
344 * Note: the reserved areas functions are not reentrant, caller is
345 * responsible for proper locking.
347 void wine_mmap_add_reserved_area( void *addr
, size_t size
)
349 struct reserved_area
*area
;
352 if (!((char *)addr
+ size
)) size
--; /* avoid wrap-around */
354 LIST_FOR_EACH( ptr
, &reserved_areas
)
356 area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
357 if (area
->base
> addr
)
359 /* try to merge with the next one */
360 if ((char *)addr
+ size
== (char *)area
->base
)
368 else if ((char *)area
->base
+ area
->size
== (char *)addr
)
370 /* merge with the previous one */
373 /* try to merge with the next one too */
374 if ((ptr
= list_next( &reserved_areas
, ptr
)))
376 struct reserved_area
*next
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
377 if ((char *)addr
+ size
== (char *)next
->base
)
379 area
->size
+= next
->size
;
380 list_remove( &next
->entry
);
388 if ((area
= malloc( sizeof(*area
) )))
392 list_add_before( ptr
, &area
->entry
);
397 /***********************************************************************
398 * wine_mmap_remove_reserved_area
400 * Remove an address range from the list of reserved areas.
401 * If 'unmap' is non-zero the range is unmapped too.
403 * Note: the reserved areas functions are not reentrant, caller is
404 * responsible for proper locking.
406 void wine_mmap_remove_reserved_area( void *addr
, size_t size
, int unmap
)
408 struct reserved_area
*area
;
411 if (!((char *)addr
+ size
)) size
--; /* avoid wrap-around */
413 ptr
= list_head( &reserved_areas
);
414 /* find the first area covering address */
417 area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
418 if ((char *)area
->base
>= (char *)addr
+ size
) break; /* outside the range */
419 if ((char *)area
->base
+ area
->size
> (char *)addr
) /* overlaps range */
421 if (area
->base
>= addr
)
423 if ((char *)area
->base
+ area
->size
> (char *)addr
+ size
)
425 /* range overlaps beginning of area only -> shrink area */
426 if (unmap
) munmap( area
->base
, (char *)addr
+ size
- (char *)area
->base
);
427 area
->size
-= (char *)addr
+ size
- (char *)area
->base
;
428 area
->base
= (char *)addr
+ size
;
433 /* range contains the whole area -> remove area completely */
434 ptr
= list_next( &reserved_areas
, ptr
);
435 if (unmap
) munmap( area
->base
, area
->size
);
436 list_remove( &area
->entry
);
443 if ((char *)area
->base
+ area
->size
> (char *)addr
+ size
)
445 /* range is in the middle of area -> split area in two */
446 struct reserved_area
*new_area
= malloc( sizeof(*new_area
) );
449 new_area
->base
= (char *)addr
+ size
;
450 new_area
->size
= (char *)area
->base
+ area
->size
- (char *)new_area
->base
;
451 list_add_after( ptr
, &new_area
->entry
);
453 else size
= (char *)area
->base
+ area
->size
- (char *)addr
;
454 area
->size
= (char *)addr
- (char *)area
->base
;
455 if (unmap
) munmap( addr
, size
);
460 /* range overlaps end of area only -> shrink area */
461 if (unmap
) munmap( addr
, (char *)area
->base
+ area
->size
- (char *)addr
);
462 area
->size
= (char *)addr
- (char *)area
->base
;
466 ptr
= list_next( &reserved_areas
, ptr
);
471 /***********************************************************************
472 * wine_mmap_is_in_reserved_area
474 * Check if the specified range is included in a reserved area.
475 * Returns 1 if range is fully included, 0 if range is not included
476 * at all, and -1 if it is only partially included.
478 * Note: the reserved areas functions are not reentrant, caller is
479 * responsible for proper locking.
481 int wine_mmap_is_in_reserved_area( void *addr
, size_t size
)
483 struct reserved_area
*area
;
486 LIST_FOR_EACH( ptr
, &reserved_areas
)
488 area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
489 if (area
->base
> addr
) break;
490 if ((char *)area
->base
+ area
->size
<= (char *)addr
) continue;
491 /* area must contain block completely */
492 if ((char *)area
->base
+ area
->size
< (char *)addr
+ size
) return -1;