kernel: Make sure the file contains data for the ReadFile test.
[wine/multimedia.git] / libs / wine / mmap.c
blob335e1489705e4dcc35012988cd91d5ebc742e5d8
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__)
144 * On Darwin, we can use the Mach call vm_allocate to allocate
145 * anonymous memory at the specified address, and then use mmap with
146 * MAP_FIXED to replace the mapping.
148 static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
149 int fildes, off_t off)
151 void *result;
152 result = addr;
153 if(vm_allocate(mach_task_self(),&result,len,0))
154 return 0;
155 else
157 result = mmap( addr, len, prot, flags | MAP_FIXED, fildes, off );
158 return result == addr;
162 #endif /* (__svr4__ || __NetBSD__) && !MAP_TRYFIXED */
165 /***********************************************************************
166 * wine_anon_mmap
168 * Portable wrapper for anonymous mmaps
170 void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
172 #ifdef HAVE_MMAP
173 static int fdzero = -1;
175 #ifdef MAP_ANON
176 flags |= MAP_ANON;
177 #else
178 if (fdzero == -1)
180 if ((fdzero = open( "/dev/zero", O_RDONLY )) == -1)
182 perror( "/dev/zero: open" );
183 exit(1);
186 #endif /* MAP_ANON */
188 #ifdef MAP_SHARED
189 flags &= ~MAP_SHARED;
190 #endif
192 /* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
193 #ifdef MAP_PRIVATE
194 flags |= MAP_PRIVATE;
195 #endif
197 if (!(flags & MAP_FIXED))
199 #ifdef __FreeBSD__
200 /* Even FreeBSD 5.3 does not properly support NULL here. */
201 if( start == NULL ) start = (void *)0x110000;
202 #endif
204 #ifdef MAP_TRYFIXED
205 /* If available, this will attempt a fixed mapping in-kernel */
206 flags |= MAP_TRYFIXED;
207 #elif defined(__svr4__) || defined(__NetBSD__) || defined(__APPLE__)
208 if ( try_mmap_fixed( start, size, prot, flags, fdzero, 0 ) )
209 return start;
210 #endif
212 return mmap( start, size, prot, flags, fdzero, 0 );
213 #else
214 return (void *)-1;
215 #endif
219 #ifdef HAVE_MMAP
221 /***********************************************************************
222 * reserve_area
224 * Reserve as much memory as possible in the given area.
225 * FIXME: probably needs a different algorithm for Solaris
227 static void reserve_area( void *addr, void *end )
229 void *ptr;
230 size_t size = (char *)end - (char *)addr;
232 if (!size) return;
234 if ((ptr = wine_anon_mmap( addr, size, PROT_NONE, MAP_NORESERVE )) != (void *)-1)
236 if (ptr == addr)
238 wine_mmap_add_reserved_area( addr, size );
239 return;
241 else munmap( ptr, size );
243 if (size > granularity_mask + 1)
245 size_t new_size = (size / 2) & ~granularity_mask;
246 reserve_area( addr, (char *)addr + new_size );
247 reserve_area( (char *)addr + new_size, end );
252 /***********************************************************************
253 * reserve_dos_area
255 * Reserve the DOS area (0x00000000-0x00110000).
257 static void reserve_dos_area(void)
259 const size_t page_size = getpagesize();
260 const size_t dos_area_size = 0x110000;
261 void *ptr;
263 /* first page has to be handled specially */
264 ptr = wine_anon_mmap( (void *)page_size, dos_area_size - page_size, PROT_NONE, MAP_NORESERVE );
265 if (ptr != (void *)page_size)
267 if (ptr != (void *)-1) munmap( ptr, dos_area_size - page_size );
268 return;
270 /* now add first page with MAP_FIXED */
271 wine_anon_mmap( NULL, page_size, PROT_NONE, MAP_NORESERVE|MAP_FIXED );
272 wine_mmap_add_reserved_area( NULL, dos_area_size );
276 /***********************************************************************
277 * mmap_init
279 void mmap_init(void)
281 struct reserved_area *area;
282 struct list *ptr;
283 #if defined(__i386__) && !defined(__FreeBSD__) /* commented out until FreeBSD gets fixed */
284 char stack;
285 char * const stack_ptr = &stack;
286 char *user_space_limit = (char *)0x80000000;
288 /* check for a reserved area starting at the user space limit */
289 /* to avoid wasting time trying to allocate it again */
290 LIST_FOR_EACH( ptr, &reserved_areas )
292 area = LIST_ENTRY( ptr, struct reserved_area, entry );
293 if ((char *)area->base > user_space_limit) break;
294 if ((char *)area->base + area->size > user_space_limit)
296 user_space_limit = (char *)area->base + area->size;
297 break;
301 if (stack_ptr >= user_space_limit)
303 char *base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) - (granularity_mask + 1);
304 if (base > user_space_limit) reserve_area( user_space_limit, base );
305 base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) + (granularity_mask + 1);
306 #ifdef linux
307 /* Linux heuristic: if the stack top is at c0000000, assume the address space */
308 /* ends there, this avoids a lot of futile allocation attempts */
309 if (base != (char *)0xc0000000)
310 #endif
311 reserve_area( base, 0 );
313 else reserve_area( user_space_limit, 0 );
314 #endif /* __i386__ */
316 /* reserve the DOS area if not already done */
318 ptr = list_head( &reserved_areas );
319 if (ptr)
321 area = LIST_ENTRY( ptr, struct reserved_area, entry );
322 if (!area->base) return; /* already reserved */
324 reserve_dos_area();
327 #else /* HAVE_MMAP */
329 void mmap_init(void)
333 #endif
335 /***********************************************************************
336 * wine_mmap_add_reserved_area
338 * Add an address range to the list of reserved areas.
339 * Caller must have made sure the range is not used by anything else.
341 * Note: the reserved areas functions are not reentrant, caller is
342 * responsible for proper locking.
344 void wine_mmap_add_reserved_area( void *addr, size_t size )
346 struct reserved_area *area;
347 struct list *ptr;
349 if (!((char *)addr + size)) size--; /* avoid wrap-around */
351 LIST_FOR_EACH( ptr, &reserved_areas )
353 area = LIST_ENTRY( ptr, struct reserved_area, entry );
354 if (area->base > addr)
356 /* try to merge with the next one */
357 if ((char *)addr + size == (char *)area->base)
359 area->base = addr;
360 area->size += size;
361 return;
363 break;
365 else if ((char *)area->base + area->size == (char *)addr)
367 /* merge with the previous one */
368 area->size += size;
370 /* try to merge with the next one too */
371 if ((ptr = list_next( &reserved_areas, ptr )))
373 struct reserved_area *next = LIST_ENTRY( ptr, struct reserved_area, entry );
374 if ((char *)addr + size == (char *)next->base)
376 area->size += next->size;
377 list_remove( &next->entry );
378 free( next );
381 return;
385 if ((area = malloc( sizeof(*area) )))
387 area->base = addr;
388 area->size = size;
389 list_add_before( ptr, &area->entry );
394 /***********************************************************************
395 * wine_mmap_remove_reserved_area
397 * Remove an address range from the list of reserved areas.
398 * If 'unmap' is non-zero the range is unmapped too.
400 * Note: the reserved areas functions are not reentrant, caller is
401 * responsible for proper locking.
403 void wine_mmap_remove_reserved_area( void *addr, size_t size, int unmap )
405 struct reserved_area *area;
406 struct list *ptr;
408 if (!((char *)addr + size)) size--; /* avoid wrap-around */
410 ptr = list_head( &reserved_areas );
411 /* find the first area covering address */
412 while (ptr)
414 area = LIST_ENTRY( ptr, struct reserved_area, entry );
415 if ((char *)area->base >= (char *)addr + size) break; /* outside the range */
416 if ((char *)area->base + area->size > (char *)addr) /* overlaps range */
418 if (area->base >= addr)
420 if ((char *)area->base + area->size > (char *)addr + size)
422 /* range overlaps beginning of area only -> shrink area */
423 if (unmap) munmap( area->base, (char *)addr + size - (char *)area->base );
424 area->size -= (char *)addr + size - (char *)area->base;
425 area->base = (char *)addr + size;
426 break;
428 else
430 /* range contains the whole area -> remove area completely */
431 ptr = list_next( &reserved_areas, ptr );
432 if (unmap) munmap( area->base, area->size );
433 list_remove( &area->entry );
434 free( area );
435 continue;
438 else
440 if ((char *)area->base + area->size > (char *)addr + size)
442 /* range is in the middle of area -> split area in two */
443 struct reserved_area *new_area = malloc( sizeof(*new_area) );
444 if (new_area)
446 new_area->base = (char *)addr + size;
447 new_area->size = (char *)area->base + area->size - (char *)new_area->base;
448 list_add_after( ptr, &new_area->entry );
450 else size = (char *)area->base + area->size - (char *)addr;
451 area->size = (char *)addr - (char *)area->base;
452 if (unmap) munmap( addr, size );
453 break;
455 else
457 /* range overlaps end of area only -> shrink area */
458 if (unmap) munmap( addr, (char *)area->base + area->size - (char *)addr );
459 area->size = (char *)addr - (char *)area->base;
463 ptr = list_next( &reserved_areas, ptr );
468 /***********************************************************************
469 * wine_mmap_is_in_reserved_area
471 * Check if the specified range is included in a reserved area.
472 * Returns 1 if range is fully included, 0 if range is not included
473 * at all, and -1 if it is only partially included.
475 * Note: the reserved areas functions are not reentrant, caller is
476 * responsible for proper locking.
478 int wine_mmap_is_in_reserved_area( void *addr, size_t size )
480 struct reserved_area *area;
481 struct list *ptr;
483 LIST_FOR_EACH( ptr, &reserved_areas )
485 area = LIST_ENTRY( ptr, struct reserved_area, entry );
486 if (area->base > addr) break;
487 if ((char *)area->base + area->size <= (char *)addr) continue;
488 /* area must contain block completely */
489 if ((char *)area->base + area->size < (char *)addr + size) return -1;
490 return 1;
492 return 0;