VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Wine/libWine/mmap.c@ 33876

Last change on this file since 33876 was 33656, checked in by vboxsync, 14 years ago

*: rebrand Sun (L)GPL disclaimers

  • Property svn:eol-style set to native
File size: 18.9 KB
Line 
1/*
2 * Wine memory mappings support
3 *
4 * Copyright 2000, 2004 Alexandre Julliard
5 *
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.
10 *
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.
15 *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21/*
22 * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
23 * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
24 * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
25 * a choice of LGPL license versions is made available with the language indicating
26 * that LGPLv2 or any later version may be used, or where a choice of which version
27 * of the LGPL is applied is otherwise unspecified.
28 */
29
30#include "config.h"
31#include "wine/port.h"
32
33#include <assert.h>
34#include <ctype.h>
35#include <fcntl.h>
36#include <stdlib.h>
37#include <stdio.h>
38#include <string.h>
39#include <sys/types.h>
40#ifdef HAVE_SYS_MMAN_H
41#include <sys/mman.h>
42#endif
43#ifdef HAVE_UNISTD_H
44# include <unistd.h>
45#endif
46#ifdef HAVE_STDINT_H
47# include <stdint.h>
48#endif
49
50#include "wine/library.h"
51#include "wine/list.h"
52
53#ifdef HAVE_MMAP
54
55struct reserved_area
56{
57 struct list entry;
58 void *base;
59 size_t size;
60};
61
62static struct list reserved_areas = LIST_INIT(reserved_areas);
63static const unsigned int granularity_mask = 0xffff; /* reserved areas have 64k granularity */
64
65#ifndef MAP_NORESERVE
66#define MAP_NORESERVE 0
67#endif
68#ifndef MAP_PRIVATE
69#define MAP_PRIVATE 0
70#endif
71#ifndef MAP_ANON
72#define MAP_ANON 0
73#endif
74
75static inline int get_fdzero(void)
76{
77 static int fd = -1;
78
79 if (MAP_ANON == 0 && fd == -1)
80 {
81 if ((fd = open( "/dev/zero", O_RDONLY )) == -1)
82 {
83 perror( "/dev/zero: open" );
84 exit(1);
85 }
86 }
87 return fd;
88}
89
90#if (defined(__svr4__) || defined(__NetBSD__)) && !defined(MAP_TRYFIXED)
91/***********************************************************************
92 * try_mmap_fixed
93 *
94 * The purpose of this routine is to emulate the behaviour of
95 * the Linux mmap() routine if a non-NULL address is passed,
96 * but the MAP_FIXED flag is not set. Linux in this case tries
97 * to place the mapping at the specified address, *unless* the
98 * range is already in use. Solaris, however, completely ignores
99 * the address argument in this case.
100 *
101 * As Wine code occasionally relies on the Linux behaviour, e.g. to
102 * be able to map non-relocatable PE executables to their proper
103 * start addresses, or to map the DOS memory to 0, this routine
104 * emulates the Linux behaviour by checking whether the desired
105 * address range is still available, and placing the mapping there
106 * using MAP_FIXED if so.
107 */
108static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
109 int fildes, off_t off)
110{
111 char * volatile result = NULL;
112 int pagesize = getpagesize();
113 pid_t pid;
114
115 /* We only try to map to a fixed address if
116 addr is non-NULL and properly aligned,
117 and MAP_FIXED isn't already specified. */
118
119 if ( !addr )
120 return 0;
121 if ( (uintptr_t)addr & (pagesize-1) )
122 return 0;
123 if ( flags & MAP_FIXED )
124 return 0;
125
126 /* We use vfork() to freeze all threads of the
127 current process. This allows us to check without
128 race condition whether the desired memory range is
129 already in use. Note that because vfork() shares
130 the address spaces between parent and child, we
131 can actually perform the mapping in the child. */
132
133 if ( (pid = vfork()) == -1 )
134 {
135 perror("try_mmap_fixed: vfork");
136 exit(1);
137 }
138 if ( pid == 0 )
139 {
140 int i;
141 char vec;
142
143 /* We call mincore() for every page in the desired range.
144 If any of these calls succeeds, the page is already
145 mapped and we must fail. */
146 for ( i = 0; i < len; i += pagesize )
147 if ( mincore( (caddr_t)addr + i, pagesize, &vec ) != -1 )
148 _exit(1);
149
150 /* Perform the mapping with MAP_FIXED set. This is safe
151 now, as none of the pages is currently in use. */
152 result = mmap( addr, len, prot, flags | MAP_FIXED, fildes, off );
153 if ( result == addr )
154 _exit(0);
155
156 if ( result != (void *) -1 ) /* This should never happen ... */
157 munmap( result, len );
158
159 _exit(1);
160 }
161
162 /* vfork() lets the parent continue only after the child
163 has exited. Furthermore, Wine sets SIGCHLD to SIG_IGN,
164 so we don't need to wait for the child. */
165
166 return result == addr;
167}
168
169#elif defined(__APPLE__)
170
171#include <mach/mach_init.h>
172#include <mach/vm_map.h>
173
174/*
175 * On Darwin, we can use the Mach call vm_allocate to allocate
176 * anonymous memory at the specified address, and then use mmap with
177 * MAP_FIXED to replace the mapping.
178 */
179static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
180 int fildes, off_t off)
181{
182 vm_address_t result = (vm_address_t)addr;
183
184 if (!vm_allocate(mach_task_self(),&result,len,0))
185 {
186 if (mmap( (void *)result, len, prot, flags | MAP_FIXED, fildes, off ) != MAP_FAILED)
187 return 1;
188 vm_deallocate(mach_task_self(),result,len);
189 }
190 return 0;
191}
192
193#endif /* (__svr4__ || __NetBSD__) && !MAP_TRYFIXED */
194
195
196/***********************************************************************
197 * wine_anon_mmap
198 *
199 * Portable wrapper for anonymous mmaps
200 */
201void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
202{
203#ifdef MAP_SHARED
204 flags &= ~MAP_SHARED;
205#endif
206
207 /* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
208 flags |= MAP_PRIVATE | MAP_ANON;
209
210 if (!(flags & MAP_FIXED))
211 {
212#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
213 /* Even FreeBSD 5.3 does not properly support NULL here. */
214 if( start == NULL ) start = (void *)0x110000;
215#endif
216
217#ifdef MAP_TRYFIXED
218 /* If available, this will attempt a fixed mapping in-kernel */
219 flags |= MAP_TRYFIXED;
220#elif defined(__svr4__) || defined(__NetBSD__) || defined(__APPLE__)
221 if ( try_mmap_fixed( start, size, prot, flags, get_fdzero(), 0 ) )
222 return start;
223#endif
224 }
225 return mmap( start, size, prot, flags, get_fdzero(), 0 );
226}
227
228
229/***********************************************************************
230 * mmap_reserve
231 *
232 * mmap wrapper used for reservations, only maps the specified address
233 */
234static inline int mmap_reserve( void *addr, size_t size )
235{
236 void *ptr;
237 int flags = MAP_PRIVATE | MAP_ANON | MAP_NORESERVE;
238
239#ifdef MAP_TRYFIXED
240 flags |= MAP_TRYFIXED;
241#elif defined(__APPLE__)
242 return try_mmap_fixed( addr, size, PROT_NONE, flags, get_fdzero(), 0 );
243#endif
244 ptr = mmap( addr, size, PROT_NONE, flags, get_fdzero(), 0 );
245 if (ptr != addr && ptr != (void *)-1) munmap( ptr, size );
246 return (ptr == addr);
247}
248
249
250/***********************************************************************
251 * reserve_area
252 *
253 * Reserve as much memory as possible in the given area.
254 */
255#ifdef __i386__
256static void reserve_area( void *addr, void *end )
257{
258 size_t size = (char *)end - (char *)addr;
259
260#if (defined(__svr4__) || defined(__NetBSD__)) && !defined(MAP_TRYFIXED)
261 /* try_mmap_fixed is inefficient when using vfork, so we need a different algorithm here */
262 /* we assume no other thread is running at this point */
263 size_t i, pagesize = getpagesize();
264 char vec;
265
266 while (size)
267 {
268 for (i = 0; i < size; i += pagesize)
269 if (mincore( (caddr_t)addr + i, pagesize, &vec ) != -1) break;
270
271 i &= ~granularity_mask;
272 if (i && mmap( addr, i, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
273 get_fdzero(), 0 ) != (void *)-1)
274 wine_mmap_add_reserved_area( addr, i );
275
276 i += granularity_mask + 1;
277 if ((char *)addr + i < (char *)addr) break; /* overflow */
278 addr = (char *)addr + i;
279 if (addr >= end) break;
280 size = (char *)end - (char *)addr;
281 }
282#else
283 if (!size) return;
284
285 if (mmap_reserve( addr, size ))
286 {
287 wine_mmap_add_reserved_area( addr, size );
288 return;
289 }
290 if (size > granularity_mask + 1)
291 {
292 size_t new_size = (size / 2) & ~granularity_mask;
293 reserve_area( addr, (char *)addr + new_size );
294 reserve_area( (char *)addr + new_size, end );
295 }
296#endif
297}
298
299
300/***********************************************************************
301 * reserve_malloc_space
302 *
303 * Solaris malloc is not smart enough to obtain space through mmap(), so try to make
304 * sure that there is some available sbrk() space before we reserve other things.
305 */
306static void reserve_malloc_space( size_t size )
307{
308#ifdef __sun
309 size_t i, count = size / 1024;
310 void **ptrs = malloc( count * sizeof(ptrs[0]) );
311
312 if (!ptrs) return;
313
314 for (i = 0; i < count; i++) if (!(ptrs[i] = malloc( 1024 ))) break;
315 if (i--) /* free everything except the last one */
316 while (i) free( ptrs[--i] );
317 free( ptrs );
318#endif
319}
320
321#endif /* __i386__ */
322
323
324/***********************************************************************
325 * reserve_dos_area
326 *
327 * Reserve the DOS area (0x00000000-0x00110000).
328 */
329static void reserve_dos_area(void)
330{
331 const size_t page_size = getpagesize();
332 const size_t dos_area_size = 0x110000;
333 void *ptr;
334
335 /* first page has to be handled specially */
336 ptr = wine_anon_mmap( (void *)page_size, dos_area_size - page_size, PROT_NONE, MAP_NORESERVE );
337 if (ptr != (void *)page_size)
338 {
339 if (ptr != (void *)-1) munmap( ptr, dos_area_size - page_size );
340 return;
341 }
342 /* now add first page with MAP_FIXED */
343 wine_anon_mmap( NULL, page_size, PROT_NONE, MAP_NORESERVE|MAP_FIXED );
344 wine_mmap_add_reserved_area( NULL, dos_area_size );
345}
346
347
348/***********************************************************************
349 * mmap_init
350 */
351void mmap_init(void)
352{
353 struct reserved_area *area;
354 struct list *ptr;
355#ifdef __i386__
356 char stack;
357 char * const stack_ptr = &stack;
358 char *user_space_limit = (char *)0x7ffe0000;
359
360 reserve_malloc_space( 8 * 1024 * 1024 );
361
362 if (!list_head( &reserved_areas ))
363 {
364 /* if we don't have a preloader, try to reserve some space below 2Gb */
365 reserve_area( (void *)0x00110000, (void *)0x40000000 );
366 }
367
368 /* check for a reserved area starting at the user space limit */
369 /* to avoid wasting time trying to allocate it again */
370 LIST_FOR_EACH( ptr, &reserved_areas )
371 {
372 area = LIST_ENTRY( ptr, struct reserved_area, entry );
373 if ((char *)area->base > user_space_limit) break;
374 if ((char *)area->base + area->size > user_space_limit)
375 {
376 user_space_limit = (char *)area->base + area->size;
377 break;
378 }
379 }
380
381 if (stack_ptr >= user_space_limit)
382 {
383 char *end = 0;
384 char *base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) - (granularity_mask + 1);
385 if (base > user_space_limit) reserve_area( user_space_limit, base );
386 base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) + (granularity_mask + 1);
387#if defined(linux) || defined(__FreeBSD__)
388 /* Heuristic: assume the stack is near the end of the address */
389 /* space, this avoids a lot of futile allocation attempts */
390 end = (char *)(((unsigned long)base + 0x0fffffff) & 0xf0000000);
391#endif
392 reserve_area( base, end );
393 }
394 else reserve_area( user_space_limit, 0 );
395#endif /* __i386__ */
396
397 /* reserve the DOS area if not already done */
398
399 ptr = list_head( &reserved_areas );
400 if (ptr)
401 {
402 area = LIST_ENTRY( ptr, struct reserved_area, entry );
403 if (!area->base) return; /* already reserved */
404 }
405 reserve_dos_area();
406}
407
408
409/***********************************************************************
410 * wine_mmap_add_reserved_area
411 *
412 * Add an address range to the list of reserved areas.
413 * Caller must have made sure the range is not used by anything else.
414 *
415 * Note: the reserved areas functions are not reentrant, caller is
416 * responsible for proper locking.
417 */
418void wine_mmap_add_reserved_area( void *addr, size_t size )
419{
420 struct reserved_area *area;
421 struct list *ptr;
422
423 if (!((char *)addr + size)) size--; /* avoid wrap-around */
424
425 LIST_FOR_EACH( ptr, &reserved_areas )
426 {
427 area = LIST_ENTRY( ptr, struct reserved_area, entry );
428 if (area->base > addr)
429 {
430 /* try to merge with the next one */
431 if ((char *)addr + size == (char *)area->base)
432 {
433 area->base = addr;
434 area->size += size;
435 return;
436 }
437 break;
438 }
439 else if ((char *)area->base + area->size == (char *)addr)
440 {
441 /* merge with the previous one */
442 area->size += size;
443
444 /* try to merge with the next one too */
445 if ((ptr = list_next( &reserved_areas, ptr )))
446 {
447 struct reserved_area *next = LIST_ENTRY( ptr, struct reserved_area, entry );
448 if ((char *)addr + size == (char *)next->base)
449 {
450 area->size += next->size;
451 list_remove( &next->entry );
452 free( next );
453 }
454 }
455 return;
456 }
457 }
458
459 if ((area = malloc( sizeof(*area) )))
460 {
461 area->base = addr;
462 area->size = size;
463 list_add_before( ptr, &area->entry );
464 }
465}
466
467
468/***********************************************************************
469 * wine_mmap_remove_reserved_area
470 *
471 * Remove an address range from the list of reserved areas.
472 * If 'unmap' is non-zero the range is unmapped too.
473 *
474 * Note: the reserved areas functions are not reentrant, caller is
475 * responsible for proper locking.
476 */
477void wine_mmap_remove_reserved_area( void *addr, size_t size, int unmap )
478{
479 struct reserved_area *area;
480 struct list *ptr;
481
482 if (!((char *)addr + size)) size--; /* avoid wrap-around */
483
484 ptr = list_head( &reserved_areas );
485 /* find the first area covering address */
486 while (ptr)
487 {
488 area = LIST_ENTRY( ptr, struct reserved_area, entry );
489 if ((char *)area->base >= (char *)addr + size) break; /* outside the range */
490 if ((char *)area->base + area->size > (char *)addr) /* overlaps range */
491 {
492 if (area->base >= addr)
493 {
494 if ((char *)area->base + area->size > (char *)addr + size)
495 {
496 /* range overlaps beginning of area only -> shrink area */
497 if (unmap) munmap( area->base, (char *)addr + size - (char *)area->base );
498 area->size -= (char *)addr + size - (char *)area->base;
499 area->base = (char *)addr + size;
500 break;
501 }
502 else
503 {
504 /* range contains the whole area -> remove area completely */
505 ptr = list_next( &reserved_areas, ptr );
506 if (unmap) munmap( area->base, area->size );
507 list_remove( &area->entry );
508 free( area );
509 continue;
510 }
511 }
512 else
513 {
514 if ((char *)area->base + area->size > (char *)addr + size)
515 {
516 /* range is in the middle of area -> split area in two */
517 struct reserved_area *new_area = malloc( sizeof(*new_area) );
518 if (new_area)
519 {
520 new_area->base = (char *)addr + size;
521 new_area->size = (char *)area->base + area->size - (char *)new_area->base;
522 list_add_after( ptr, &new_area->entry );
523 }
524 else size = (char *)area->base + area->size - (char *)addr;
525 area->size = (char *)addr - (char *)area->base;
526 if (unmap) munmap( addr, size );
527 break;
528 }
529 else
530 {
531 /* range overlaps end of area only -> shrink area */
532 if (unmap) munmap( addr, (char *)area->base + area->size - (char *)addr );
533 area->size = (char *)addr - (char *)area->base;
534 }
535 }
536 }
537 ptr = list_next( &reserved_areas, ptr );
538 }
539}
540
541
542/***********************************************************************
543 * wine_mmap_is_in_reserved_area
544 *
545 * Check if the specified range is included in a reserved area.
546 * Returns 1 if range is fully included, 0 if range is not included
547 * at all, and -1 if it is only partially included.
548 *
549 * Note: the reserved areas functions are not reentrant, caller is
550 * responsible for proper locking.
551 */
552int wine_mmap_is_in_reserved_area( void *addr, size_t size )
553{
554 struct reserved_area *area;
555 struct list *ptr;
556
557 LIST_FOR_EACH( ptr, &reserved_areas )
558 {
559 area = LIST_ENTRY( ptr, struct reserved_area, entry );
560 if (area->base > addr) break;
561 if ((char *)area->base + area->size <= (char *)addr) continue;
562 /* area must contain block completely */
563 if ((char *)area->base + area->size < (char *)addr + size) return -1;
564 return 1;
565 }
566 return 0;
567}
568
569
570/***********************************************************************
571 * wine_mmap_enum_reserved_areas
572 *
573 * Enumerate the list of reserved areas, sorted by addresses.
574 * If enum_func returns a non-zero value, enumeration is stopped and the value is returned.
575 *
576 * Note: the reserved areas functions are not reentrant, caller is
577 * responsible for proper locking.
578 */
579int wine_mmap_enum_reserved_areas( int (*enum_func)(void *base, size_t size, void *arg), void *arg,
580 int top_down )
581{
582 int ret = 0;
583 struct list *ptr;
584
585 if (top_down)
586 {
587 for (ptr = reserved_areas.prev; ptr != &reserved_areas; ptr = ptr->prev)
588 {
589 struct reserved_area *area = LIST_ENTRY( ptr, struct reserved_area, entry );
590 if ((ret = enum_func( area->base, area->size, arg ))) break;
591 }
592 }
593 else
594 {
595 for (ptr = reserved_areas.next; ptr != &reserved_areas; ptr = ptr->next)
596 {
597 struct reserved_area *area = LIST_ENTRY( ptr, struct reserved_area, entry );
598 if ((ret = enum_func( area->base, area->size, arg ))) break;
599 }
600 }
601 return ret;
602}
603
604#else /* HAVE_MMAP */
605
606void mmap_init(void)
607{
608}
609
610#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette