winefile: Add the ability to save window position to the registry.
[wine.git] / libs / wine / mmap.c
blob3d25f264f1110a7332e143dfb41e75bd994a9c24
1 /*
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
21 #include "config.h"
22 #include "wine/port.h"
24 #include <assert.h>
25 #include <ctype.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #ifdef HAVE_SYS_MMAN_H
31 #include <sys/mman.h>
32 #endif
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36 #ifdef HAVE_STDINT_H
37 # include <stdint.h>
38 #endif
40 #include "wine/library.h"
41 #include "wine/list.h"
43 struct reserved_area
45 struct list entry;
46 void *base;
47 size_t size;
50 static struct list reserved_areas = LIST_INIT(reserved_areas);
51 static const int granularity_mask = 0xffff; /* reserved areas have 64k granularity */
53 #ifndef MAP_NORESERVE
54 #define MAP_NORESERVE 0
55 #endif
57 #ifndef HAVE_MMAP
58 static inline int munmap( void *ptr, size_t size ) { return 0; }
59 #endif
62 #if (defined(__svr4__) || defined(__NetBSD__)) && !defined(MAP_TRYFIXED)
63 /***********************************************************************
64 * try_mmap_fixed
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();
85 pid_t pid;
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. */
91 if ( !addr )
92 return 0;
93 if ( (uintptr_t)addr & (pagesize-1) )
94 return 0;
95 if ( flags & MAP_FIXED )
96 return 0;
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");
108 exit(1);
110 if ( pid == 0 )
112 int i;
113 char vec;
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 )
120 _exit(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 )
126 _exit(0);
128 if ( result != (void *) -1 ) /* This should never happen ... */
129 munmap( result, len );
131 _exit(1);
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)
159 return 1;
160 vm_deallocate(mach_task_self(),result,len);
162 return 0;
165 #endif /* (__svr4__ || __NetBSD__) && !MAP_TRYFIXED */
168 /***********************************************************************
169 * wine_anon_mmap
171 * Portable wrapper for anonymous mmaps
173 void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
175 #ifdef HAVE_MMAP
176 static int fdzero = -1;
178 #ifdef MAP_ANON
179 flags |= MAP_ANON;
180 #else
181 if (fdzero == -1)
183 if ((fdzero = open( "/dev/zero", O_RDONLY )) == -1)
185 perror( "/dev/zero: open" );
186 exit(1);
189 #endif /* MAP_ANON */
191 #ifdef MAP_SHARED
192 flags &= ~MAP_SHARED;
193 #endif
195 /* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
196 #ifdef MAP_PRIVATE
197 flags |= MAP_PRIVATE;
198 #endif
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;
205 #endif
207 #ifdef MAP_TRYFIXED
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 ) )
212 return start;
213 #endif
215 return mmap( start, size, prot, flags, fdzero, 0 );
216 #else
217 return (void *)-1;
218 #endif
222 #ifdef HAVE_MMAP
224 /***********************************************************************
225 * reserve_area
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 )
232 void *ptr;
233 size_t size = (char *)end - (char *)addr;
235 if (!size) return;
237 if ((ptr = wine_anon_mmap( addr, size, PROT_NONE, MAP_NORESERVE )) != (void *)-1)
239 if (ptr == addr)
241 wine_mmap_add_reserved_area( addr, size );
242 return;
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 /***********************************************************************
256 * reserve_dos_area
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;
264 void *ptr;
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 );
271 return;
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 /***********************************************************************
280 * mmap_init
282 void mmap_init(void)
284 struct reserved_area *area;
285 struct list *ptr;
286 #if defined(__i386__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) /* commented out until FreeBSD gets fixed */
287 char stack;
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;
300 break;
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);
309 #ifdef linux
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)
313 #endif
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 );
322 if (ptr)
324 area = LIST_ENTRY( ptr, struct reserved_area, entry );
325 if (!area->base) return; /* already reserved */
327 reserve_dos_area();
330 #else /* HAVE_MMAP */
332 void mmap_init(void)
336 #endif
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;
350 struct list *ptr;
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)
362 area->base = addr;
363 area->size += size;
364 return;
366 break;
368 else if ((char *)area->base + area->size == (char *)addr)
370 /* merge with the previous one */
371 area->size += size;
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 );
381 free( next );
384 return;
388 if ((area = malloc( sizeof(*area) )))
390 area->base = addr;
391 area->size = size;
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;
409 struct list *ptr;
411 if (!((char *)addr + size)) size--; /* avoid wrap-around */
413 ptr = list_head( &reserved_areas );
414 /* find the first area covering address */
415 while (ptr)
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;
429 break;
431 else
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 );
437 free( area );
438 continue;
441 else
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) );
447 if (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 );
456 break;
458 else
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;
484 struct list *ptr;
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;
493 return 1;
495 return 0;