[Tizen] Unify dnetmemoryenumlib terms to match the codebase (#291)
[platform/upstream/coreclr.git] / src / utilcode / lazycow.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 // --------------------------------------------------------------------------------
5 // LazyCOW.cpp
6 //
7
8 #include "stdafx.h"
9
10 #include "pedecoder.h"
11 #include "volatile.h"
12 #include "lazycow.h"
13
14 #ifdef FEATURE_LAZY_COW_PAGES
15
16 // 
17 // We can't use the hosted ClrVirtualProtect, because EnsureWritablePages is called in places where we can't
18 // call into the host.
19 //
20 #ifdef VirtualProtect
21 #undef VirtualProtect
22 #endif
23
24 #ifdef VirtualAlloc
25 #undef VirtualAlloc
26 #endif
27
28 #ifdef VirtualFree
29 #undef VirtualFree
30 #endif
31
32 LONG* g_pCOWPageMap;    // one bit for each page in the virtual address space
33 LONG  g_COWPageMapMap;  // one bit for each page in g_pCOWPageMap.
34
35 LONG* EnsureCOWPageMapAllocated()
36 {
37     if (g_pCOWPageMap == NULL)
38     {
39         // We store one bit for every page in the virtual address space.  We may need to revisit this for 64-bit. :)
40         MEMORYSTATUSEX stats;
41         stats.dwLength = sizeof(stats);
42         if (GlobalMemoryStatusEx(&stats))
43         {
44             _ASSERTE(stats.ullTotalVirtual < 0x100000000ULL);
45
46             SIZE_T mapSize = (SIZE_T)((stats.ullTotalVirtual / GetOsPageSize()) / 8);
47             _ASSERTE(mapSize / GetOsPageSize() <= 32); // g_COWPageMapMap can only track 32 pages
48
49             // Note that VirtualAlloc will zero-fill the pages for us.
50             LONG* pMap = (LONG*)VirtualAlloc(
51                 NULL,
52                 mapSize,
53                 MEM_RESERVE,
54                 PAGE_READWRITE);
55
56             if (pMap != NULL)
57             {
58                 if (NULL != InterlockedCompareExchangeT(&g_pCOWPageMap, pMap, NULL))
59                     VirtualFree(pMap, 0, MEM_RELEASE);
60             }
61         }
62     }
63     return g_pCOWPageMap;
64 }
65
66 bool EnsureCOWPageMapElementAllocated(LONG* elem)
67 {
68     _ASSERTE(elem > g_pCOWPageMap);
69     _ASSERTE(g_pCOWPageMap != NULL);
70
71     size_t offset = (size_t)elem - (size_t)g_pCOWPageMap;
72     size_t page = offset / GetOsPageSize();
73     
74     _ASSERTE(page < 32);
75     int bit = (int)(1 << page);
76     
77     if (!(g_COWPageMapMap & bit))
78     {
79         if (!VirtualAlloc(elem, 1, MEM_COMMIT, PAGE_READWRITE))
80             return false;
81
82         InterlockedOr(&g_COWPageMapMap, bit);
83     }
84
85     return true;
86 }
87
88 bool IsCOWPageMapElementAllocated(LONG* elem)
89 {
90     _ASSERTE(elem >= g_pCOWPageMap);
91     _ASSERTE(g_pCOWPageMap != NULL);
92
93     size_t offset = (size_t)elem - (size_t)g_pCOWPageMap;
94     size_t page = offset / GetOsPageSize();
95     
96     _ASSERTE(page < 32);
97     int bit = (int)(1 << page);
98     
99     return (g_COWPageMapMap & bit) != 0;
100 }
101
102 bool SetCOWPageBits(BYTE* pStart, size_t len, bool value)
103 {
104     _ASSERTE(len > 0);
105
106     // we don't need a barrier here, since:
107     //  a) all supported hardware maintains ordering of dependent reads
108     //  b) it's ok if additional reads happen, because this never changes
109     //     once initialized.
110     LONG* pCOWPageMap = g_pCOWPageMap;
111
112     //
113     // Write the bits in 32-bit chunks, to avoid doing one interlocked instruction for each bit.
114     //
115     size_t page = (size_t)pStart / GetOsPageSize();
116     size_t lastPage = (size_t)(pStart+len-1) / GetOsPageSize();
117     size_t elem = page / 32;
118     LONG bits = 0;
119     do
120     {
121         bits |= 1 << (page % 32);
122
123         ++page;
124
125         //
126         // if we've moved to a new element of the map, or we've covered every page,
127         // we need to write out the already-accumulated element.
128         //
129         size_t newElem = page / 32;
130         if (page > lastPage || newElem != elem)
131         {
132             LONG* pElem = &pCOWPageMap[elem];
133             if (!EnsureCOWPageMapElementAllocated(pElem))
134                 return false;
135
136             if (value)
137                 InterlockedOr(&pCOWPageMap[elem], bits);
138             else
139                 InterlockedAnd(&pCOWPageMap[elem], ~bits);
140
141             elem = newElem;
142             bits = 0;
143         }
144     }
145     while (page <= lastPage);
146
147     return true;
148 }
149
150 bool SetCOWPageBitsForImage(PEDecoder * pImage, bool value)
151 {
152     if (!pImage->HasNativeHeader())
153         return true;
154
155     bool success = true;
156
157     IMAGE_SECTION_HEADER* pSection;
158     BYTE * pStart;
159     size_t len;
160
161     pSection = pImage->FindSection(".data");
162     if (pSection != NULL)
163     {
164         pStart = (BYTE*) dac_cast<TADDR>(pImage->GetBase()) + pSection->VirtualAddress;
165         len = pSection->Misc.VirtualSize;
166
167         if (!SetCOWPageBits(pStart, len, value))
168             success = false;
169     }
170
171     pSection = pImage->FindSection(".xdata");
172     if (pSection != NULL)
173     {
174         pStart = (BYTE*) dac_cast<TADDR>(pImage->GetBase()) + pSection->VirtualAddress;
175         len = pSection->Misc.VirtualSize;
176
177         if (!SetCOWPageBits(pStart, len, value))
178             success = false;
179     }
180
181     return success;
182 }
183
184 bool AreAnyCOWPageBitsSet(BYTE* pStart, size_t len)
185 {
186     LONG* pCOWPageMap = g_pCOWPageMap;
187     if (pCOWPageMap == NULL)
188         return false;
189
190     _ASSERTE(len > 0);
191     size_t page = (size_t)pStart / GetOsPageSize();
192     size_t lastPage = (size_t)(pStart+len-1) / GetOsPageSize();
193     do
194     {
195         LONG* pElem = &pCOWPageMap[page / 32];
196         if (IsCOWPageMapElementAllocated(pElem) && 
197             (*pElem & (1 << (page %32))))
198         {
199             return true;
200         }
201         ++page;
202     }
203     while (page <= lastPage);
204
205     return false;
206 }
207
208
209 void AllocateLazyCOWPages(PEDecoder * pImage)
210 {
211     //
212     // Note: it's ok to call AllocateLazyCOWPages multiple times for the same loaded image.
213     // This will result in any already-writable pages being incorrectly marked as read-only
214     // in our records, but that just means we'll call VirtualProtect one more time.
215     //
216     // However, FreeLazyCOWPages must be called just once per loaded image, when it is actually.  
217     // unloaded.  Otherwise we will lose track of the COW pages in that image while it might 
218     // still be accessible.
219     //
220
221     if (!EnsureCOWPageMapAllocated())
222         ThrowOutOfMemory();
223
224     if (!SetCOWPageBitsForImage(pImage, true))
225         ThrowOutOfMemory();
226 }
227
228 void FreeLazyCOWPages(PEDecoder * pImage)
229 {
230     //
231     // Must be called just once per image; see note in AllocateLazyCOWPages.
232     //
233     SetCOWPageBitsForImage(pImage, false);
234 }
235
236 bool IsInReadOnlyLazyCOWPage(void* p)
237 {
238     return AreAnyCOWPageBitsSet((BYTE*)p, 1);
239 }
240
241
242 bool MakeWritable(BYTE* pStart, size_t len, DWORD protect)
243 {
244     DWORD oldProtect;
245     if (!VirtualProtect(pStart, len, protect, &oldProtect))
246         return false;
247     INDEBUG(bool result = ) SetCOWPageBits(pStart, len, false);
248     _ASSERTE(result); // we already set these, so we must be able to clear them.
249     return true;
250 }
251
252 bool EnsureWritablePagesNoThrow(void* p, size_t len)
253 {
254     BYTE* pStart = (BYTE*)p;
255
256     if (len == 0)
257         return true;
258
259     if (!AreAnyCOWPageBitsSet(pStart, len))
260         return true;
261
262     return MakeWritable(pStart, len, PAGE_READWRITE);
263 }
264
265 void EnsureWritablePages(void* p, size_t len)
266 {
267     CONTRACTL {
268         THROWS;
269     } CONTRACTL_END;
270
271     BYTE* pStart = (BYTE*)p;
272
273     if (len == 0)
274         return;
275
276     if (!AreAnyCOWPageBitsSet(pStart, len))
277         return;
278
279     if (!MakeWritable(pStart, len, PAGE_READWRITE))
280         ThrowOutOfMemory();
281 }
282
283 bool EnsureWritableExecutablePagesNoThrow(void* p, size_t len)
284 {
285     BYTE* pStart = (BYTE*) p;
286
287     if (len == 0)
288         return true;
289
290     if (!AreAnyCOWPageBitsSet(pStart, len))
291         return true;
292
293     return MakeWritable(pStart, len, PAGE_EXECUTE_READWRITE);
294 }
295
296 void EnsureWritableExecutablePages(void* p, size_t len)
297 {
298     CONTRACTL {
299         THROWS;
300     } CONTRACTL_END;
301
302     BYTE* pStart = (BYTE*) p;
303
304     if (len == 0)
305         return;
306
307     if (!AreAnyCOWPageBitsSet(pStart, len))
308         return;
309
310     if (!MakeWritable(pStart, len, PAGE_EXECUTE_READWRITE))
311         ThrowOutOfMemory();
312 }
313
314 #endif // FEATURE_LAZY_COW_PAGES
315
316