libwine: New naming scheme for 16-bit builtin dlls.
[wine] / libs / wine / mmap.c
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
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
39
40 #include "wine/library.h"
41 #include "wine/list.h"
42
43 struct reserved_area
44 {
45     struct list entry;
46     void       *base;
47     size_t      size;
48 };
49
50 static struct list reserved_areas = LIST_INIT(reserved_areas);
51 static const int granularity_mask = 0xffff;  /* reserved areas have 64k granularity */
52
53 #ifndef MAP_NORESERVE
54 #define MAP_NORESERVE 0
55 #endif
56
57 #ifndef HAVE_MMAP
58 static inline int munmap( void *ptr, size_t size ) { return 0; }
59 #endif
60
61
62 #if (defined(__svr4__) || defined(__NetBSD__)) && !defined(MAP_TRYFIXED)
63 /***********************************************************************
64  *             try_mmap_fixed
65  *
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.
72  *
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.
79  */
80 static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
81                            int fildes, off_t off)
82 {
83     char * volatile result = NULL;
84     int pagesize = getpagesize();
85     pid_t pid;
86
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. */
90
91     if ( !addr )
92         return 0;
93     if ( (uintptr_t)addr & (pagesize-1) )
94         return 0;
95     if ( flags & MAP_FIXED )
96         return 0;
97
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. */
104
105     if ( (pid = vfork()) == -1 )
106     {
107         perror("try_mmap_fixed: vfork");
108         exit(1);
109     }
110     if ( pid == 0 )
111     {
112         int i;
113         char vec;
114
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);
121
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);
127
128         if ( result != (void *) -1 ) /* This should never happen ... */
129             munmap( result, len );
130
131        _exit(1);
132     }
133
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. */
137
138     return result == addr;
139 }
140
141 #elif defined(__APPLE__)
142
143 #include <mach/mach_init.h>
144 #include <mach/vm_map.h>
145
146 /*
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.
150  */
151 static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
152                            int fildes, off_t off)
153 {
154     vm_address_t result = (vm_address_t)addr;
155
156     if (!vm_allocate(mach_task_self(),&result,len,0))
157     {
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);
161     }
162     return 0;
163 }
164
165 #endif  /* (__svr4__ || __NetBSD__) && !MAP_TRYFIXED */
166
167
168 /***********************************************************************
169  *              wine_anon_mmap
170  *
171  * Portable wrapper for anonymous mmaps
172  */
173 void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
174 {
175 #ifdef HAVE_MMAP
176     static int fdzero = -1;
177
178 #ifdef MAP_ANON
179     flags |= MAP_ANON;
180 #else
181     if (fdzero == -1)
182     {
183         if ((fdzero = open( "/dev/zero", O_RDONLY )) == -1)
184         {
185             perror( "/dev/zero: open" );
186             exit(1);
187         }
188     }
189 #endif  /* MAP_ANON */
190
191 #ifdef MAP_SHARED
192     flags &= ~MAP_SHARED;
193 #endif
194
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
199
200     if (!(flags & MAP_FIXED))
201     {
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
206
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
214     }
215     return mmap( start, size, prot, flags, fdzero, 0 );
216 #else
217     return (void *)-1;
218 #endif
219 }
220
221
222 #ifdef HAVE_MMAP
223
224 /***********************************************************************
225  *           reserve_area
226  *
227  * Reserve as much memory as possible in the given area.
228  * FIXME: probably needs a different algorithm for Solaris
229  */
230 static void reserve_area( void *addr, void *end )
231 {
232     void *ptr;
233     size_t size = (char *)end - (char *)addr;
234
235     if (!size) return;
236
237     if ((ptr = wine_anon_mmap( addr, size, PROT_NONE, MAP_NORESERVE )) != (void *)-1)
238     {
239         if (ptr == addr)
240         {
241             wine_mmap_add_reserved_area( addr, size );
242             return;
243         }
244         else munmap( ptr, size );
245     }
246     if (size > granularity_mask + 1)
247     {
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 );
251     }
252 }
253
254
255 /***********************************************************************
256  *           reserve_dos_area
257  *
258  * Reserve the DOS area (0x00000000-0x00110000).
259  */
260 static void reserve_dos_area(void)
261 {
262     const size_t page_size = getpagesize();
263     const size_t dos_area_size = 0x110000;
264     void *ptr;
265
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)
269     {
270         if (ptr != (void *)-1) munmap( ptr, dos_area_size - page_size );
271         return;
272     }
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 );
276 }
277
278
279 /***********************************************************************
280  *           mmap_init
281  */
282 void mmap_init(void)
283 {
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;
290
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 )
294     {
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)
298         {
299             user_space_limit = (char *)area->base + area->size;
300             break;
301         }
302     }
303
304     if (stack_ptr >= user_space_limit)
305     {
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 );
315     }
316     else reserve_area( user_space_limit, 0 );
317 #endif /* __i386__ */
318
319     /* reserve the DOS area if not already done */
320
321     ptr = list_head( &reserved_areas );
322     if (ptr)
323     {
324         area = LIST_ENTRY( ptr, struct reserved_area, entry );
325         if (!area->base) return;  /* already reserved */
326     }
327     reserve_dos_area();
328 }
329
330 #else /* HAVE_MMAP */
331
332 void mmap_init(void)
333 {
334 }
335
336 #endif
337
338 /***********************************************************************
339  *           wine_mmap_add_reserved_area
340  *
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.
343  *
344  * Note: the reserved areas functions are not reentrant, caller is
345  * responsible for proper locking.
346  */
347 void wine_mmap_add_reserved_area( void *addr, size_t size )
348 {
349     struct reserved_area *area;
350     struct list *ptr;
351
352     if (!((char *)addr + size)) size--;  /* avoid wrap-around */
353
354     LIST_FOR_EACH( ptr, &reserved_areas )
355     {
356         area = LIST_ENTRY( ptr, struct reserved_area, entry );
357         if (area->base > addr)
358         {
359             /* try to merge with the next one */
360             if ((char *)addr + size == (char *)area->base)
361             {
362                 area->base = addr;
363                 area->size += size;
364                 return;
365             }
366             break;
367         }
368         else if ((char *)area->base + area->size == (char *)addr)
369         {
370             /* merge with the previous one */
371             area->size += size;
372
373             /* try to merge with the next one too */
374             if ((ptr = list_next( &reserved_areas, ptr )))
375             {
376                 struct reserved_area *next = LIST_ENTRY( ptr, struct reserved_area, entry );
377                 if ((char *)addr + size == (char *)next->base)
378                 {
379                     area->size += next->size;
380                     list_remove( &next->entry );
381                     free( next );
382                 }
383             }
384             return;
385         }
386     }
387
388     if ((area = malloc( sizeof(*area) )))
389     {
390         area->base = addr;
391         area->size = size;
392         list_add_before( ptr, &area->entry );
393     }
394 }
395
396
397 /***********************************************************************
398  *           wine_mmap_remove_reserved_area
399  *
400  * Remove an address range from the list of reserved areas.
401  * If 'unmap' is non-zero the range is unmapped too.
402  *
403  * Note: the reserved areas functions are not reentrant, caller is
404  * responsible for proper locking.
405  */
406 void wine_mmap_remove_reserved_area( void *addr, size_t size, int unmap )
407 {
408     struct reserved_area *area;
409     struct list *ptr;
410
411     if (!((char *)addr + size)) size--;  /* avoid wrap-around */
412
413     ptr = list_head( &reserved_areas );
414     /* find the first area covering address */
415     while (ptr)
416     {
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 */
420         {
421             if (area->base >= addr)
422             {
423                 if ((char *)area->base + area->size > (char *)addr + size)
424                 {
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;
430                 }
431                 else
432                 {
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;
439                 }
440             }
441             else
442             {
443                 if ((char *)area->base + area->size > (char *)addr + size)
444                 {
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)
448                     {
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 );
452                     }
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;
457                 }
458                 else
459                 {
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;
463                 }
464             }
465         }
466         ptr = list_next( &reserved_areas, ptr );
467     }
468 }
469
470
471 /***********************************************************************
472  *           wine_mmap_is_in_reserved_area
473  *
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.
477  *
478  * Note: the reserved areas functions are not reentrant, caller is
479  * responsible for proper locking.
480  */
481 int wine_mmap_is_in_reserved_area( void *addr, size_t size )
482 {
483     struct reserved_area *area;
484     struct list *ptr;
485
486     LIST_FOR_EACH( ptr, &reserved_areas )
487     {
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;
494     }
495     return 0;
496 }