VirtualBox

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

Last change on this file since 23571 was 19678, checked in by vboxsync, 16 years ago

opengl: update wine to 1.1.21, add d3d9.dll to build list

  • Property svn:eol-style set to native
File size: 18.7 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 * Sun 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, Sun 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 /* check for a reserved area starting at the user space limit */
363 /* to avoid wasting time trying to allocate it again */
364 LIST_FOR_EACH( ptr, &reserved_areas )
365 {
366 area = LIST_ENTRY( ptr, struct reserved_area, entry );
367 if ((char *)area->base > user_space_limit) break;
368 if ((char *)area->base + area->size > user_space_limit)
369 {
370 user_space_limit = (char *)area->base + area->size;
371 break;
372 }
373 }
374
375 if (stack_ptr >= user_space_limit)
376 {
377 char *end = 0;
378 char *base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) - (granularity_mask + 1);
379 if (base > user_space_limit) reserve_area( user_space_limit, base );
380 base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) + (granularity_mask + 1);
381#if defined(linux) || defined(__FreeBSD__)
382 /* Heuristic: assume the stack is near the end of the address */
383 /* space, this avoids a lot of futile allocation attempts */
384 end = (char *)(((unsigned long)base + 0x0fffffff) & 0xf0000000);
385#endif
386 reserve_area( base, end );
387 }
388 else reserve_area( user_space_limit, 0 );
389#endif /* __i386__ */
390
391 /* reserve the DOS area if not already done */
392
393 ptr = list_head( &reserved_areas );
394 if (ptr)
395 {
396 area = LIST_ENTRY( ptr, struct reserved_area, entry );
397 if (!area->base) return; /* already reserved */
398 }
399 reserve_dos_area();
400}
401
402
403/***********************************************************************
404 * wine_mmap_add_reserved_area
405 *
406 * Add an address range to the list of reserved areas.
407 * Caller must have made sure the range is not used by anything else.
408 *
409 * Note: the reserved areas functions are not reentrant, caller is
410 * responsible for proper locking.
411 */
412void wine_mmap_add_reserved_area( void *addr, size_t size )
413{
414 struct reserved_area *area;
415 struct list *ptr;
416
417 if (!((char *)addr + size)) size--; /* avoid wrap-around */
418
419 LIST_FOR_EACH( ptr, &reserved_areas )
420 {
421 area = LIST_ENTRY( ptr, struct reserved_area, entry );
422 if (area->base > addr)
423 {
424 /* try to merge with the next one */
425 if ((char *)addr + size == (char *)area->base)
426 {
427 area->base = addr;
428 area->size += size;
429 return;
430 }
431 break;
432 }
433 else if ((char *)area->base + area->size == (char *)addr)
434 {
435 /* merge with the previous one */
436 area->size += size;
437
438 /* try to merge with the next one too */
439 if ((ptr = list_next( &reserved_areas, ptr )))
440 {
441 struct reserved_area *next = LIST_ENTRY( ptr, struct reserved_area, entry );
442 if ((char *)addr + size == (char *)next->base)
443 {
444 area->size += next->size;
445 list_remove( &next->entry );
446 free( next );
447 }
448 }
449 return;
450 }
451 }
452
453 if ((area = malloc( sizeof(*area) )))
454 {
455 area->base = addr;
456 area->size = size;
457 list_add_before( ptr, &area->entry );
458 }
459}
460
461
462/***********************************************************************
463 * wine_mmap_remove_reserved_area
464 *
465 * Remove an address range from the list of reserved areas.
466 * If 'unmap' is non-zero the range is unmapped too.
467 *
468 * Note: the reserved areas functions are not reentrant, caller is
469 * responsible for proper locking.
470 */
471void wine_mmap_remove_reserved_area( void *addr, size_t size, int unmap )
472{
473 struct reserved_area *area;
474 struct list *ptr;
475
476 if (!((char *)addr + size)) size--; /* avoid wrap-around */
477
478 ptr = list_head( &reserved_areas );
479 /* find the first area covering address */
480 while (ptr)
481 {
482 area = LIST_ENTRY( ptr, struct reserved_area, entry );
483 if ((char *)area->base >= (char *)addr + size) break; /* outside the range */
484 if ((char *)area->base + area->size > (char *)addr) /* overlaps range */
485 {
486 if (area->base >= addr)
487 {
488 if ((char *)area->base + area->size > (char *)addr + size)
489 {
490 /* range overlaps beginning of area only -> shrink area */
491 if (unmap) munmap( area->base, (char *)addr + size - (char *)area->base );
492 area->size -= (char *)addr + size - (char *)area->base;
493 area->base = (char *)addr + size;
494 break;
495 }
496 else
497 {
498 /* range contains the whole area -> remove area completely */
499 ptr = list_next( &reserved_areas, ptr );
500 if (unmap) munmap( area->base, area->size );
501 list_remove( &area->entry );
502 free( area );
503 continue;
504 }
505 }
506 else
507 {
508 if ((char *)area->base + area->size > (char *)addr + size)
509 {
510 /* range is in the middle of area -> split area in two */
511 struct reserved_area *new_area = malloc( sizeof(*new_area) );
512 if (new_area)
513 {
514 new_area->base = (char *)addr + size;
515 new_area->size = (char *)area->base + area->size - (char *)new_area->base;
516 list_add_after( ptr, &new_area->entry );
517 }
518 else size = (char *)area->base + area->size - (char *)addr;
519 area->size = (char *)addr - (char *)area->base;
520 if (unmap) munmap( addr, size );
521 break;
522 }
523 else
524 {
525 /* range overlaps end of area only -> shrink area */
526 if (unmap) munmap( addr, (char *)area->base + area->size - (char *)addr );
527 area->size = (char *)addr - (char *)area->base;
528 }
529 }
530 }
531 ptr = list_next( &reserved_areas, ptr );
532 }
533}
534
535
536/***********************************************************************
537 * wine_mmap_is_in_reserved_area
538 *
539 * Check if the specified range is included in a reserved area.
540 * Returns 1 if range is fully included, 0 if range is not included
541 * at all, and -1 if it is only partially included.
542 *
543 * Note: the reserved areas functions are not reentrant, caller is
544 * responsible for proper locking.
545 */
546int wine_mmap_is_in_reserved_area( void *addr, size_t size )
547{
548 struct reserved_area *area;
549 struct list *ptr;
550
551 LIST_FOR_EACH( ptr, &reserved_areas )
552 {
553 area = LIST_ENTRY( ptr, struct reserved_area, entry );
554 if (area->base > addr) break;
555 if ((char *)area->base + area->size <= (char *)addr) continue;
556 /* area must contain block completely */
557 if ((char *)area->base + area->size < (char *)addr + size) return -1;
558 return 1;
559 }
560 return 0;
561}
562
563
564/***********************************************************************
565 * wine_mmap_enum_reserved_areas
566 *
567 * Enumerate the list of reserved areas, sorted by addresses.
568 * If enum_func returns a non-zero value, enumeration is stopped and the value is returned.
569 *
570 * Note: the reserved areas functions are not reentrant, caller is
571 * responsible for proper locking.
572 */
573int wine_mmap_enum_reserved_areas( int (*enum_func)(void *base, size_t size, void *arg), void *arg,
574 int top_down )
575{
576 int ret = 0;
577 struct list *ptr;
578
579 if (top_down)
580 {
581 for (ptr = reserved_areas.prev; ptr != &reserved_areas; ptr = ptr->prev)
582 {
583 struct reserved_area *area = LIST_ENTRY( ptr, struct reserved_area, entry );
584 if ((ret = enum_func( area->base, area->size, arg ))) break;
585 }
586 }
587 else
588 {
589 for (ptr = reserved_areas.next; ptr != &reserved_areas; ptr = ptr->next)
590 {
591 struct reserved_area *area = LIST_ENTRY( ptr, struct reserved_area, entry );
592 if ((ret = enum_func( area->base, area->size, arg ))) break;
593 }
594 }
595 return ret;
596}
597
598#else /* HAVE_MMAP */
599
600void mmap_init(void)
601{
602}
603
604#endif
Note: See TracBrowser for help on using the repository browser.

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