1 /* -----------------------------------------------------------------------------
3 * (c) The University of Glasgow 2006-2007
5 * OS-specific memory management
7 * ---------------------------------------------------------------------------*/
9 // This is non-posix compliant.
10 // #include "PosixSource.h"
20 #ifdef HAVE_SYS_TYPES_H
21 #include <sys/types.h>
23 #ifdef HAVE_SYS_MMAN_H
36 #include <mach/mach.h>
37 #include <mach/vm_map.h>
40 static caddr_t next_request = 0;
44 next_request = (caddr_t)RtsFlags.GcFlags.heapBase;
47 /* -----------------------------------------------------------------------------
50 On Unix-like systems, we use mmap() to allocate our memory. We
51 want memory in chunks of MBLOCK_SIZE, and aligned on an MBLOCK_SIZE
52 boundary. The mmap() interface doesn't give us this level of
53 control, so we have to use some heuristics.
55 In the general case, if we want a block of n megablocks, then we
56 allocate n+1 and trim off the slop from either side (using
57 munmap()) to get an aligned chunk of size n. However, the next
58 time we'll try to allocate directly after the previously allocated
59 chunk, on the grounds that this is aligned and likely to be free.
60 If it turns out that we were wrong, we have to munmap() and try
61 again using the general method.
63 Note on posix_memalign(): this interface is available on recent
64 systems and appears to provide exactly what we want. However, it
65 turns out not to be as good as our mmap() implementation, because
66 it wastes extra space (using double the address space, in a test on
67 x86_64/Linux). The problem seems to be that posix_memalign()
68 returns memory that can be free()'d, so the library must store
69 extra information along with the allocated block, thus messing up
70 the alignment. Hence, we don't use posix_memalign() for now.
72 -------------------------------------------------------------------------- */
74 // A wrapper around mmap(), to abstract away from OS differences in
75 // the mmap() interface.
78 my_mmap (void *addr, lnat size)
82 #if defined(solaris2_HOST_OS) || defined(irix_HOST_OS)
84 int fd = open("/dev/zero",O_RDONLY);
85 ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
89 ret = mmap(addr, size, PROT_READ | PROT_WRITE,
90 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
92 // Without MAP_FIXED, Apple's mmap ignores addr.
93 // With MAP_FIXED, it overwrites already mapped regions, whic
94 // mmap(0, ... MAP_FIXED ...) is worst of all: It unmaps the program text
95 // and replaces it with zeroes, causing instant death.
96 // This behaviour seems to be conformant with IEEE Std 1003.1-2001.
97 // Let's just use the underlying Mach Microkernel calls directly,
98 // they're much nicer.
100 kern_return_t err = 0;
102 if(addr) // try to allocate at adress
103 err = vm_allocate(mach_task_self(),(vm_address_t*) &ret, size, FALSE);
104 if(!addr || err) // try to allocate anywhere
105 err = vm_allocate(mach_task_self(),(vm_address_t*) &ret, size, TRUE);
108 // don't know what the error codes mean exactly, assume it's
109 // not our problem though.
110 errorBelch("memory allocation failed (requested %lu bytes)", size);
111 stg_exit(EXIT_FAILURE);
113 vm_protect(mach_task_self(),(vm_address_t)ret,size,FALSE,VM_PROT_READ|VM_PROT_WRITE);
116 ret = mmap(addr, size, PROT_READ | PROT_WRITE,
117 MAP_ANON | MAP_PRIVATE, -1, 0);
120 if (ret == (void *)-1) {
121 if (errno == ENOMEM ||
122 (errno == EINVAL && sizeof(void*)==4 && size >= 0xc0000000)) {
123 // If we request more than 3Gig, then we get EINVAL
124 // instead of ENOMEM (at least on Linux).
125 errorBelch("out of memory (requested %lu bytes)", size);
126 stg_exit(EXIT_FAILURE);
128 barf("getMBlock: mmap: %s", strerror(errno));
135 // Implements the general case: allocate a chunk of memory of 'size'
139 gen_map_mblocks (lnat size)
144 // Try to map a larger block, and take the aligned portion from
145 // it (unmap the rest).
147 ret = my_mmap(0, size);
149 // unmap the slop bits around the chunk we allocated
150 slop = (W_)ret & MBLOCK_MASK;
152 if (munmap((void*)ret, MBLOCK_SIZE - slop) == -1) {
153 barf("gen_map_mblocks: munmap failed");
155 if (slop > 0 && munmap((void*)(ret+size-slop), slop) == -1) {
156 barf("gen_map_mblocks: munmap failed");
159 // ToDo: if we happened to get an aligned block, then don't
160 // unmap the excess, just use it. For this to work, you
161 // need to keep in mind the following:
162 // * Calling my_mmap() with an 'addr' arg pointing to
163 // already my_mmap()ed space is OK and won't fail.
164 // * If my_mmap() can't satisfy the request at the
165 // given 'next_request' address in getMBlocks(), that
166 // you unmap the extra mblock mmap()ed here (or simply
167 // satisfy yourself that the slop introduced isn't worth
171 // next time, try after the block we just got.
172 ret += MBLOCK_SIZE - slop;
180 lnat size = MBLOCK_SIZE * (lnat)n;
182 if (next_request == 0) {
183 // use gen_map_mblocks the first time.
184 ret = gen_map_mblocks(size);
186 ret = my_mmap(next_request, size);
188 if (((W_)ret & MBLOCK_MASK) != 0) {
190 #if 0 // defined(DEBUG)
191 errorBelch("warning: getMBlock: misaligned block %p returned when allocating %d megablock(s) at %p", ret, n, next_request);
194 // unmap this block...
195 if (munmap(ret, size) == -1) {
196 barf("getMBlock: munmap failed");
198 // and do it the hard way
199 ret = gen_map_mblocks(size);
202 // Next time, we'll try to allocate right after the block we just got.
203 // ToDo: check that we haven't already grabbed the memory at next_request
204 next_request = ret + size;
209 void osFreeMBlocks(char *addr, nat n)
211 munmap(addr, n * MBLOCK_SIZE);
214 void osReleaseFreeMemory(void) {
215 /* Nothing to do on POSIX */
218 void osFreeAllMBlocks(void)
222 for (mblock = getFirstMBlock();
224 mblock = getNextMBlock(mblock)) {
225 munmap(mblock, MBLOCK_SIZE);
229 lnat getPageSize (void)
231 static lnat pageSize = 0;
236 ret = sysconf(_SC_PAGESIZE);
238 barf("getPageSize: cannot get page size");
244 void setExecutable (void *p, lnat len, rtsBool exec)
246 StgWord pageSize = getPageSize();
248 /* malloced memory isn't executable by default on OpenBSD */
249 StgWord mask = ~(pageSize - 1);
250 StgWord startOfFirstPage = ((StgWord)p ) & mask;
251 StgWord startOfLastPage = ((StgWord)p + len - 1) & mask;
252 StgWord size = startOfLastPage - startOfFirstPage + pageSize;
253 if (mprotect((void*)startOfFirstPage, (size_t)size,
254 (exec ? PROT_EXEC : 0) | PROT_READ | PROT_WRITE) != 0) {
255 barf("setExecutable: failed to protect 0x%p\n", p);