OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 30 matching lines...) Expand all Loading... |
41 #include <sys/mman.h> | 41 #include <sys/mman.h> |
42 | 42 |
43 #ifndef MADV_FREE | 43 #ifndef MADV_FREE |
44 #define MADV_FREE MADV_DONTNEED | 44 #define MADV_FREE MADV_DONTNEED |
45 #endif | 45 #endif |
46 | 46 |
47 #ifndef MAP_ANONYMOUS | 47 #ifndef MAP_ANONYMOUS |
48 #define MAP_ANONYMOUS MAP_ANON | 48 #define MAP_ANONYMOUS MAP_ANON |
49 #endif | 49 #endif |
50 | 50 |
| 51 // On POSIX memmap uses a nearby address if the hint address is blocked. |
| 52 static const bool kHintIsAdvisory = true; |
| 53 |
51 #elif OS(WIN) | 54 #elif OS(WIN) |
52 | 55 |
53 #include <windows.h> | 56 #include <windows.h> |
54 | 57 |
| 58 // VirtualAlloc will fail if allocation at the hint address is blocked. |
| 59 static const bool kHintIsAdvisory = false; |
| 60 |
55 #else | 61 #else |
56 #error Unknown OS | 62 #error Unknown OS |
57 #endif // OS(POSIX) | 63 #endif // OS(POSIX) |
58 | 64 |
59 namespace WTF { | 65 namespace WTF { |
60 | 66 |
61 // This simple internal function wraps the OS-specific page allocation call so | 67 // This internal function wraps the OS-specific page allocation call. The |
62 // that it behaves consistently: the address is a hint and if it cannot be used, | 68 // behavior of the hint address is determined by the kHintIsAdvisory constant. |
63 // the allocation will be placed elsewhere. | 69 // If true, a non-zero hint is advisory and the returned address may differ from |
64 static void* systemAllocPages(void* addr, size_t len, PageAccessibilityConfigura
tion pageAccessibility) | 70 // the hint. If false, the hint is mandatory and a successful allocation will |
| 71 // not differ from the hint. |
| 72 static void* systemAllocPages(void* hint, size_t len, PageAccessibilityConfigura
tion pageAccessibility) |
65 { | 73 { |
66 ASSERT(!(len & kPageAllocationGranularityOffsetMask)); | 74 ASSERT(!(len & kPageAllocationGranularityOffsetMask)); |
67 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & kPageAllocationGranularityOffse
tMask)); | 75 ASSERT(!(reinterpret_cast<uintptr_t>(hint) & kPageAllocationGranularityOffse
tMask)); |
68 void* ret; | 76 void* ret; |
69 #if OS(WIN) | 77 #if OS(WIN) |
70 int accessFlag = pageAccessibility == PageAccessible ? PAGE_READWRITE : PAGE
_NOACCESS; | 78 DWORD accessFlag = pageAccessibility == PageAccessible ? PAGE_READWRITE : PA
GE_NOACCESS; |
71 ret = VirtualAlloc(addr, len, MEM_RESERVE | MEM_COMMIT, accessFlag); | 79 ret = VirtualAlloc(hint, len, MEM_RESERVE | MEM_COMMIT, accessFlag); |
72 if (!ret) | |
73 ret = VirtualAlloc(0, len, MEM_RESERVE | MEM_COMMIT, accessFlag); | |
74 #else | 80 #else |
75 int accessFlag = pageAccessibility == PageAccessible ? (PROT_READ | PROT_WRI
TE) : PROT_NONE; | 81 int accessFlag = pageAccessibility == PageAccessible ? (PROT_READ | PROT_WRI
TE) : PROT_NONE; |
76 ret = mmap(addr, len, accessFlag, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); | 82 ret = mmap(hint, len, accessFlag, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); |
77 if (ret == MAP_FAILED) | 83 if (ret == MAP_FAILED) |
78 ret = 0; | 84 ret = 0; |
79 #endif | 85 #endif |
80 return ret; | 86 return ret; |
81 } | 87 } |
82 | 88 |
83 static bool trimMapping(void* baseAddr, size_t baseLen, void* trimAddr, size_t t
rimLen) | 89 // Trims base to given length and alignment. Windows returns null on failure and
frees base. |
| 90 static void* trimMapping(void *base, size_t baseLen, size_t trimLen, uintptr_t a
lign, PageAccessibilityConfiguration pageAccessibility) |
84 { | 91 { |
85 #if OS(WIN) | 92 size_t preSlack = reinterpret_cast<uintptr_t>(base) & (align - 1); |
86 return false; | 93 if (preSlack) |
87 #else | 94 preSlack = align - preSlack; |
88 char* basePtr = static_cast<char*>(baseAddr); | 95 size_t postSlack = baseLen - preSlack - trimLen; |
89 char* trimPtr = static_cast<char*>(trimAddr); | 96 ASSERT(baseLen >= trimLen || preSlack || postSlack); |
90 ASSERT(trimPtr >= basePtr); | 97 ASSERT(preSlack < baseLen); |
91 ASSERT(trimPtr + trimLen <= basePtr + baseLen); | 98 ASSERT(postSlack < baseLen); |
92 size_t preLen = trimPtr - basePtr; | 99 void* ret = base; |
93 if (preLen) { | 100 |
94 int ret = munmap(basePtr, preLen); | 101 #if OS(POSIX) // On POSIX we can resize the allocation run. |
95 RELEASE_ASSERT(!ret); | 102 (void) pageAccessibility; |
| 103 if (preSlack) { |
| 104 int res = munmap(base, preSlack); |
| 105 RELEASE_ASSERT(!res); |
| 106 ret = reinterpret_cast<char*>(base) + preSlack; |
96 } | 107 } |
97 size_t postLen = (basePtr + baseLen) - (trimPtr + trimLen); | 108 if (postSlack) { |
98 if (postLen) { | 109 int res = munmap(reinterpret_cast<char*>(ret) + trimLen, postSlack); |
99 int ret = munmap(trimPtr + trimLen, postLen); | 110 RELEASE_ASSERT(!res); |
100 RELEASE_ASSERT(!ret); | |
101 } | 111 } |
102 return true; | 112 #else // On Windows we can't resize the allocation run. |
| 113 if (preSlack || postSlack) { |
| 114 ret = reinterpret_cast<char*>(base) + preSlack; |
| 115 freePages(base, baseLen); |
| 116 ret = systemAllocPages(ret, trimLen, pageAccessibility); |
| 117 } |
103 #endif | 118 #endif |
| 119 |
| 120 return ret; |
104 } | 121 } |
105 | 122 |
106 void* allocPages(void* addr, size_t len, size_t align, PageAccessibilityConfigur
ation pageAccessibility) | 123 void* allocPages(void* addr, size_t len, size_t align, PageAccessibilityConfigur
ation pageAccessibility) |
107 { | 124 { |
108 ASSERT(len >= kPageAllocationGranularity); | 125 ASSERT(len >= kPageAllocationGranularity); |
109 ASSERT(!(len & kPageAllocationGranularityOffsetMask)); | 126 ASSERT(!(len & kPageAllocationGranularityOffsetMask)); |
110 ASSERT(align >= kPageAllocationGranularity); | 127 ASSERT(align >= kPageAllocationGranularity); |
111 ASSERT(!(align & kPageAllocationGranularityOffsetMask)); | 128 ASSERT(!(align & kPageAllocationGranularityOffsetMask)); |
112 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & kPageAllocationGranularityOffse
tMask)); | 129 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & kPageAllocationGranularityOffse
tMask)); |
113 size_t alignOffsetMask = align - 1; | 130 uintptr_t alignOffsetMask = align - 1; |
114 size_t alignBaseMask = ~alignOffsetMask; | 131 uintptr_t alignBaseMask = ~alignOffsetMask; |
115 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & alignOffsetMask)); | 132 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & alignOffsetMask)); |
| 133 |
116 // If the client passed null as the address, choose a good one. | 134 // If the client passed null as the address, choose a good one. |
117 if (!addr) { | 135 if (!addr) { |
118 addr = getRandomPageBase(); | 136 addr = getRandomPageBase(); |
119 addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & align
BaseMask); | 137 addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & align
BaseMask); |
120 } | 138 } |
121 | 139 |
122 // The common case, which is also the least work we can do, is that the | 140 // First try to force an exact-size, aligned allocation from our random base
. |
123 // address and length are suitable. Just try it. | 141 for (int count = 0; count < 3; ++count) { |
124 void* ret = systemAllocPages(addr, len, pageAccessibility); | 142 void* ret = systemAllocPages(addr, len, pageAccessibility); |
125 // If the alignment is to our liking, we're done. | 143 if (kHintIsAdvisory || ret) { |
126 if (!ret || !(reinterpret_cast<uintptr_t>(ret) & alignOffsetMask)) | 144 // If the alignment is to our liking, we're done. |
127 return ret; | 145 if (!(reinterpret_cast<uintptr_t>(ret)& alignOffsetMask)) |
| 146 return ret; |
| 147 freePages(ret, len); |
| 148 #if CPU(32BIT) |
| 149 addr = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(ret)+ali
gn) & alignBaseMask); |
| 150 #endif |
| 151 } else if (!addr) { // We know we're OOM when an unhinted allocation fai
ls. |
| 152 return nullptr; |
128 | 153 |
129 // Annoying. Unmap and map a larger range to be sure to succeed on the | 154 } else { |
130 // second, slower attempt. | 155 #if CPU(32BIT) |
131 freePages(ret, len); | 156 addr = reinterpret_cast<char*>(addr) + align; |
| 157 #endif |
| 158 } |
132 | 159 |
133 size_t tryLen = len + (align - kPageAllocationGranularity); | 160 #if !CPU(32BIT) // Keep trying random addresses on systems that have a large add
ress space. |
134 RELEASE_ASSERT(tryLen > len); | |
135 | |
136 // We loop to cater for the unlikely case where another thread maps on top | |
137 // of the aligned location we choose. | |
138 int count = 0; | |
139 while (count++ < 100) { | |
140 ret = systemAllocPages(addr, tryLen, pageAccessibility); | |
141 if (!ret) | |
142 return 0; | |
143 // We can now try and trim out a subset of the mapping. | |
144 addr = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(ret) + align
OffsetMask) & alignBaseMask); | |
145 | |
146 // On POSIX systems, we can trim the oversized mapping to fit exactly. | |
147 // This will always work on POSIX systems. | |
148 if (trimMapping(ret, tryLen, addr, len)) | |
149 return addr; | |
150 | |
151 // On Windows, you can't trim an existing mapping so we unmap and remap | |
152 // a subset. We used to do for all platforms, but OSX 10.8 has a | |
153 // broken mmap() that ignores address hints for valid, unused addresses. | |
154 freePages(ret, tryLen); | |
155 ret = systemAllocPages(addr, len, pageAccessibility); | |
156 if (ret == addr || !ret) | |
157 return ret; | |
158 | |
159 // Unlikely race / collision. Do the simple thing and just start again. | |
160 freePages(ret, len); | |
161 addr = getRandomPageBase(); | 161 addr = getRandomPageBase(); |
162 addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & align
BaseMask); | 162 addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & align
BaseMask); |
| 163 #endif |
163 } | 164 } |
164 IMMEDIATE_CRASH(); | 165 |
165 return 0; | 166 // Map a larger allocation so we can force alignment, but continue randomizi
ng only on 64-bit POSIX. |
| 167 size_t tryLen = len + (align - kPageAllocationGranularity); |
| 168 RELEASE_ASSERT(tryLen >= len); |
| 169 void* ret; |
| 170 |
| 171 do { |
| 172 // Don't continue to burn cycles on mandatory hints (Windows). |
| 173 addr = kHintIsAdvisory ? getRandomPageBase() : nullptr; |
| 174 ret = systemAllocPages(addr, tryLen, pageAccessibility); |
| 175 // The retries are for Windows, where a race can steal our mapping on resize
. |
| 176 } while (ret && !(ret = trimMapping(ret, tryLen, len, align, pageAccessibili
ty))); |
| 177 |
| 178 return ret; |
166 } | 179 } |
167 | 180 |
168 void freePages(void* addr, size_t len) | 181 void freePages(void* addr, size_t len) |
169 { | 182 { |
170 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & kPageAllocationGranularityOffse
tMask)); | 183 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & kPageAllocationGranularityOffse
tMask)); |
171 ASSERT(!(len & kPageAllocationGranularityOffsetMask)); | 184 ASSERT(!(len & kPageAllocationGranularityOffsetMask)); |
172 #if OS(POSIX) | 185 #if OS(POSIX) |
173 int ret = munmap(addr, len); | 186 int ret = munmap(addr, len); |
174 RELEASE_ASSERT(!ret); | 187 RELEASE_ASSERT(!ret); |
175 #else | 188 #else |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
233 #else | 246 #else |
234 (void) addr; | 247 (void) addr; |
235 (void) len; | 248 (void) len; |
236 // TODO(cevans): implement this using MEM_RESET for Windows, once we've | 249 // TODO(cevans): implement this using MEM_RESET for Windows, once we've |
237 // decided that the semantics are a match. | 250 // decided that the semantics are a match. |
238 #endif | 251 #endif |
239 } | 252 } |
240 | 253 |
241 } // namespace WTF | 254 } // namespace WTF |
242 | 255 |
OLD | NEW |