2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * NaCl Service Runtime memory allocation code
35 #include "native_client/include/portability.h"
41 #include "native_client/service_runtime/win/mman.h"
43 #include "native_client/service_runtime/nacl_config.h"
44 #include "native_client/service_runtime/nacl_log.h"
45 #include "native_client/service_runtime/sel_memory.h"
46 #include "native_client/service_runtime/sel_util.h"
47 #include "native_client/service_runtime/win/xlate_system_error.h"
50 * NaCl_page_free: free pages allocated with NaCl_page_alloc.
51 * Must start at allocation granularity (NACL_MAP_PAGESIZE) and
52 * number of bytes must be a multiple of allocation granularity.
54 void NaCl_page_free(void *p
,
59 end_addr
= (void *) (((char *) p
) + num_bytes
);
60 while (p
< end_addr
) {
61 if (!VirtualFree(p
, 0, MEM_RELEASE
)) {
63 "NaCl_page_free: VirtualFree(0x%08x, 0, MEM_RELEASE) failed\n",
66 p
= (void *) (((char *) p
) + NACL_MAP_PAGESIZE
);
70 int NaCl_page_alloc(void **p
,
83 * We have to allocate every 64KB -- the windows allocation
84 * granularity -- because VirtualFree will only accept an address
85 * that was returned by a call to VirtualAlloc. NB: memory pages
86 * put into the address space via MapViewOfFile{,Ex} must be
87 * released by UnmapViewOfFile. Thus, in order for us to open up a
88 * hole in the NaCl application's address space to map in a file, we
89 * must allocate the entire address space in 64KB chunks, so we can
90 * later pick an arbitrary range of addresses (in 64KB chunks) to
91 * free up and map in a file later.
93 * First, we verify via GetSystemInfo that the allocation
94 * granularity matches NACL_MAP_PAGESIZE.
96 * Next, we VirtualAlloc the entire chunk desired. This essentially
97 * asks the kernel where there is space in the virtual address
98 * space. Then, we free this back, and repeat the allocation
99 * starting at the returned address, but in 64KB chunks. If any of
100 * these smaller allocations fail, we roll back and try again.
103 NaClLog(3, "NaCl_page_alloc(*, 0x%x)\n", num_bytes
);
104 GetSystemInfo(&sys_info
);
105 if (NACL_PAGESIZE
!= sys_info
.dwPageSize
) {
106 NaClLog(2, "page size is 0x%x; expected 0x%x\n",
110 if (NACL_MAP_PAGESIZE
!= sys_info
.dwAllocationGranularity
) {
111 NaClLog(0, "allocation granularity is 0x%x; expected 0x%x\n",
112 sys_info
.dwAllocationGranularity
,
117 * Round allocation request up to next NACL_MAP_PAGESIZE. This is
118 * assumed to have taken place in NaCl_page_free.
120 num_bytes
= NaClRoundAllocPage(num_bytes
);
122 for (attempt_count
= 0;
123 attempt_count
< NACL_MEMORY_ALLOC_RETRY_MAX
;
126 addr
= VirtualAlloc(NULL
,
129 PAGE_EXECUTE_READWRITE
);
133 " VirtualAlloc(*,0x%x,MEM_COMMIT,PAGE_EXECUTE_READWRITE)"
140 " VirtualAlloc(*,0x%x,MEM_COMMIT,PAGE_EXECUTE_READWRITE)"
141 " succeeded, 0x%08x,"
142 " releasing and re-allocating in 64K chunks....\n"),
144 (void) VirtualFree(addr
, 0, MEM_RELEASE
);
146 * We now know [addr, addr + num_bytes) is available in our addr space.
147 * Get that memory again, but in 64KB chunks.
149 end_addr
= (void *) (((char *) addr
) + num_bytes
);
152 chunk
= (void *) (((char *) chunk
) + NACL_MAP_PAGESIZE
)) {
153 if (NULL
== VirtualAlloc(chunk
,
155 MEM_COMMIT
| MEM_RESERVE
,
156 PAGE_EXECUTE_READWRITE
)) {
157 NaClLog(0, ("NaCl_page_alloc: re-allocation failed at 0x%08x,"
159 chunk
, GetLastError());
162 unroll
= (void *) (((char *) unroll
) + NACL_MAP_PAGESIZE
)) {
163 (void) VirtualFree(unroll
, 0, MEM_RELEASE
);
169 NaClLog(2, "NaCl_page_alloc: *p = 0x%08x, returning success.\n", addr
);
179 uintptr_t NaClProtectChunkSize(uintptr_t start
,
184 chunk_end
= NaClRoundAllocPage(start
+ 1);
185 if (chunk_end
> end
) {
188 return chunk_end
- start
;
192 * This is critical to make the text region non-writable, and the data
193 * region read/write but no exec. Of course, some kernels do not
194 * respect the lack of PROT_EXEC.
196 int NaCl_mprotect(void *addr
,
200 uintptr_t start_addr
;
203 uintptr_t cur_chunk_size
;
204 DWORD newProtection
, oldProtection
;
207 NaClLog(2, "NaCl_mprotect(0x%08x, 0x%x, 0x%x)\n",
208 (uintptr_t) addr
, len
, prot
);
214 start_addr
= (uintptr_t) addr
;
215 if (!NaClIsPageMultiple(start_addr
)) {
216 NaClLog(2, "NaCl_mprotect: start address not a multiple of page size\n");
219 if (!NaClIsPageMultiple(len
)) {
220 NaClLog(2, "NaCl_mprotect: length not a multiple of page size\n");
223 end_addr
= start_addr
+ len
;
229 newProtection
= PAGE_EXECUTE
;
232 case PROT_EXEC
|PROT_READ
:
234 newProtection
= PAGE_EXECUTE_READ
;
237 case PROT_EXEC
|PROT_READ
|PROT_WRITE
:
239 newProtection
= PAGE_EXECUTE_READWRITE
;
244 newProtection
= PAGE_READONLY
;
247 case PROT_READ
|PROT_WRITE
:
249 newProtection
= PAGE_READWRITE
;
254 newProtection
= PAGE_NOACCESS
;
259 NaClLog(2, "NaCl_mprotect: invalid protection mode\n");
263 NaClLog(2, "NaCl_mprotect: newProtection = 0x%x\n", newProtection
);
265 * VirtualProtect region cannot span allocations: all addresses from
266 * [lpAddress, lpAddress+dwSize) must be in one region of memory
267 * returned from VirtualAlloc or VirtualAllocEx
269 for (cur_addr
= start_addr
,
270 cur_chunk_size
= NaClProtectChunkSize(cur_addr
, end_addr
);
272 cur_addr
+= cur_chunk_size
,
273 cur_chunk_size
= NaClProtectChunkSize(cur_addr
, end_addr
)) {
274 NaClLog(4, "NaCl_mprotect: VirtualProtect(0x%08x, 0x%x, 0x%x, *)\n",
275 cur_addr
, cur_chunk_size
, newProtection
);
276 res
= VirtualProtect((void*) cur_addr
, cur_chunk_size
,
277 newProtection
, &oldProtection
);
279 NaClLog(2, "NaCl_mprotect: ... failed\n");
280 return -NaClXlateSystemError(GetLastError());
283 NaClLog(2, "NaCl_mprotect: done\n");
287 int NaCl_madvise(void *start
,
292 uintptr_t start_addr
;
295 uintptr_t cur_chunk_size
;
298 * MADV_DONTNEED and MADV_NORMAL are needed
300 NaClLog(2, "NaCl_madvise(0x%08x, 0x%x, 0x%x)\n",
301 (uintptr_t) start
, length
, advice
);
304 start_addr
= (uintptr_t) start
;
305 if (!NaClIsPageMultiple(start_addr
)) {
307 "NaCl_madvise: start address not a multiple of page size\n");
310 if (!NaClIsPageMultiple(length
)) {
311 NaClLog(2, "NaCl_madvise: length not a multiple of page size\n");
314 end_addr
= start_addr
+ length
;
315 for (cur_addr
= start_addr
,
316 cur_chunk_size
= NaClProtectChunkSize(cur_addr
, end_addr
);
318 cur_addr
+= cur_chunk_size
,
319 cur_chunk_size
= NaClProtectChunkSize(cur_addr
, end_addr
)) {
321 ("NaCl_madvise: MADV_DONTNEED"
322 " -> VirtualAlloc(0x%08x, 0x%x, MEM_RESET, PAGE_NOACCESS)\n"),
323 cur_addr
, cur_chunk_size
);
324 if (NULL
== VirtualAlloc((void*) cur_addr
, cur_chunk_size
, MEM_RESET
,
326 err
= NaClXlateSystemError(GetLastError());
327 NaClLog(2, "NaCl_madvise: VirtualAlloc failed: 0x%x\n", err
);
333 memset(start
, 0, length
);
338 NaClLog(2, "NaCl_madvise: done\n");