Portability fix.
[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 #endif  /* (__svr4__ || __NetBSD__) && !MAP_TRYFIXED */
141
142
143 /***********************************************************************
144  *              wine_anon_mmap
145  *
146  * Portable wrapper for anonymous mmaps
147  */
148 void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
149 {
150 #ifdef HAVE_MMAP
151     static int fdzero = -1;
152
153 #ifdef MAP_ANON
154     flags |= MAP_ANON;
155 #else
156     if (fdzero == -1)
157     {
158         if ((fdzero = open( "/dev/zero", O_RDONLY )) == -1)
159         {
160             perror( "/dev/zero: open" );
161             exit(1);
162         }
163     }
164 #endif  /* MAP_ANON */
165
166 #ifdef MAP_SHARED
167     flags &= ~MAP_SHARED;
168 #endif
169
170     /* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
171 #ifdef MAP_PRIVATE
172     flags |= MAP_PRIVATE;
173 #endif
174
175     if (!(flags & MAP_FIXED))
176     {
177 #ifdef MAP_TRYFIXED
178         /* If available, this will attempt a fixed mapping in-kernel */
179         flags |= MAP_TRYFIXED;
180 #elif defined(__svr4__) || defined(__NetBSD__)
181         if ( try_mmap_fixed( start, size, prot, flags, fdzero, 0 ) )
182             return start;
183 #endif
184     }
185     return mmap( start, size, prot, flags, fdzero, 0 );
186 #else
187     return (void *)-1;
188 #endif
189 }
190
191
192 #ifdef __i386__
193
194 /***********************************************************************
195  *           reserve_area
196  *
197  * Reserve as much memory as possible in the given area.
198  * FIXME: probably needs a different algorithm for Solaris
199  */
200 static void reserve_area( void *addr, void *end )
201 {
202     void *ptr;
203     size_t size = (char *)end - (char *)addr;
204     struct list *prev;
205     struct reserved_area *area;
206
207     if ((ptr = wine_anon_mmap( addr, size, PROT_NONE, MAP_NORESERVE )) != (void *)-1)
208     {
209         if (ptr == addr)
210         {
211             if (!end) size--;  /* avoid wrap-around */
212             /* try to merge it with the previous one */
213             if ((prev = list_tail( &reserved_areas )))
214             {
215                 area = LIST_ENTRY( prev, struct reserved_area, entry );
216                 if (area && (char *)area->base + area->size == (char *)ptr)
217                 {
218                     area->size += size;
219                     return;
220                 }
221             }
222             /* create a new area */
223             if ((area = malloc( sizeof(*area) )))
224             {
225                 area->base = addr;
226                 area->size = size;
227                 list_add_tail( &reserved_areas, &area->entry );
228                 return;
229             }
230         }
231         else munmap( ptr, size );
232     }
233     if (size > granularity_mask + 1)
234     {
235         size_t new_size = (size / 2) & ~granularity_mask;
236         reserve_area( addr, (char *)addr + new_size );
237         reserve_area( (char *)addr + new_size, end );
238     }
239 }
240
241
242 /***********************************************************************
243  *           mmap_init
244  */
245 void mmap_init(void)
246 {
247     static char * const user_space_limit = (char *)0x80000000;
248     char stack;
249     char * const stack_ptr = &stack;
250
251     if (stack_ptr >= user_space_limit)
252     {
253         char *base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) - (granularity_mask + 1);
254         if (base > user_space_limit) reserve_area( user_space_limit, base );
255         base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) + (granularity_mask + 1);
256 #ifdef linux
257         /* Linux heuristic: if the stack top is at c0000000, assume the address space */
258         /* ends there, this avoids a lot of futile allocation attempts */
259         if (base != (char *)0xc0000000)
260 #endif
261             reserve_area( base, 0 );
262     }
263     else reserve_area( user_space_limit, 0 );
264 }
265
266 #else /* __i386__ */
267
268 void mmap_init(void)
269 {
270 }
271
272 #endif
273
274 /***********************************************************************
275  *           wine_mmap_add_reserved_area
276  *
277  * Add an address range to the list of reserved areas.
278  * Caller must have made sure the range is not used by anything else.
279  *
280  * Note: the reserved areas functions are not reentrant, caller is
281  * responsible for proper locking.
282  */
283 void wine_mmap_add_reserved_area( void *addr, size_t size )
284 {
285     struct reserved_area *area;
286     struct list *ptr;
287
288     if (!((char *)addr + size)) size--;  /* avoid wrap-around */
289
290 #ifdef HAVE_MMAP
291     /* blow away existing mappings */
292     wine_anon_mmap( addr, size, PROT_NONE, MAP_NORESERVE | MAP_FIXED );
293 #endif
294
295     LIST_FOR_EACH( ptr, &reserved_areas )
296     {
297         area = LIST_ENTRY( ptr, struct reserved_area, entry );
298         if (area->base > addr)
299         {
300             /* try to merge with the next one */
301             if ((char *)addr + size == (char *)area->base)
302             {
303                 area->base = addr;
304                 area->size += size;
305                 return;
306             }
307             break;
308         }
309         else if ((char *)area->base + area->size == (char *)addr)
310         {
311             /* merge with the previous one */
312             area->size += size;
313
314             /* try to merge with the next one too */
315             if ((ptr = list_next( &reserved_areas, ptr )))
316             {
317                 struct reserved_area *next = LIST_ENTRY( ptr, struct reserved_area, entry );
318                 if ((char *)addr + size == (char *)next->base)
319                 {
320                     area->size += next->size;
321                     list_remove( &next->entry );
322                     free( next );
323                 }
324             }
325             return;
326         }
327     }
328
329     if ((area = malloc( sizeof(*area) )))
330     {
331         area->base = addr;
332         area->size = size;
333         list_add_before( ptr, &area->entry );
334     }
335 }
336
337
338 /***********************************************************************
339  *           wine_mmap_remove_reserved_area
340  *
341  * Remove an address range from the list of reserved areas.
342  * If 'unmap' is non-zero the range is unmapped too.
343  *
344  * Note: the reserved areas functions are not reentrant, caller is
345  * responsible for proper locking.
346  */
347 void wine_mmap_remove_reserved_area( void *addr, size_t size, int unmap )
348 {
349     struct reserved_area *area;
350     struct list *ptr;
351
352     if (!((char *)addr + size)) size--;  /* avoid wrap-around */
353
354     ptr = list_head( &reserved_areas );
355     /* find the first area covering address */
356     while (ptr)
357     {
358         area = LIST_ENTRY( ptr, struct reserved_area, entry );
359         if ((char *)area->base >= (char *)addr + size) break;  /* outside the range */
360         if ((char *)area->base + area->size > (char *)addr)  /* overlaps range */
361         {
362             if (area->base >= addr)
363             {
364                 if ((char *)area->base + area->size > (char *)addr + size)
365                 {
366                     /* range overlaps beginning of area only -> shrink area */
367                     if (unmap) munmap( area->base, (char *)addr + size - (char *)area->base );
368                     area->size -= (char *)addr + size - (char *)area->base;
369                     area->base = (char *)addr + size;
370                     break;
371                 }
372                 else
373                 {
374                     /* range contains the whole area -> remove area completely */
375                     ptr = list_next( &reserved_areas, ptr );
376                     if (unmap) munmap( area->base, area->size );
377                     list_remove( &area->entry );
378                     free( area );
379                     continue;
380                 }
381             }
382             else
383             {
384                 if ((char *)area->base + area->size > (char *)addr + size)
385                 {
386                     /* range is in the middle of area -> split area in two */
387                     struct reserved_area *new_area = malloc( sizeof(*new_area) );
388                     if (new_area)
389                     {
390                         new_area->base = (char *)addr + size;
391                         new_area->size = (char *)area->base + area->size - (char *)new_area->base;
392                         list_add_after( ptr, &new_area->entry );
393                     }
394                     else size = (char *)area->base + area->size - (char *)addr;
395                     area->size = (char *)addr - (char *)area->base;
396                     if (unmap) munmap( addr, size );
397                     break;
398                 }
399                 else
400                 {
401                     /* range overlaps end of area only -> shrink area */
402                     if (unmap) munmap( addr, (char *)area->base + area->size - (char *)addr );
403                     area->size = (char *)addr - (char *)area->base;
404                 }
405             }
406         }
407         ptr = list_next( &reserved_areas, ptr );
408     }
409 }
410
411
412 /***********************************************************************
413  *           wine_mmap_is_in_reserved_area
414  *
415  * Check if the specified range is included in a reserved area.
416  * Returns 1 if range is fully included, 0 if range is not included
417  * at all, and -1 if it is only partially included.
418  *
419  * Note: the reserved areas functions are not reentrant, caller is
420  * responsible for proper locking.
421  */
422 int wine_mmap_is_in_reserved_area( void *addr, size_t size )
423 {
424     struct reserved_area *area;
425     struct list *ptr;
426
427     LIST_FOR_EACH( ptr, &reserved_areas )
428     {
429         area = LIST_ENTRY( ptr, struct reserved_area, entry );
430         if (area->base > addr) break;
431         if ((char *)area->base + area->size <= (char *)addr) continue;
432         /* area must contain block completely */
433         if ((char *)area->base + area->size < (char *)addr + size) return -1;
434         return 1;
435     }
436     return 0;
437 }