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 | /*++ |
6 | |
7 | |
8 | |
9 | Module Name: |
10 | |
11 | include/pal/virtual.h |
12 | |
13 | Abstract: |
14 | Header file for virtual memory management. |
15 | |
16 | |
17 | |
18 | --*/ |
19 | |
20 | #ifndef _PAL_VIRTUAL_H_ |
21 | #define _PAL_VIRTUAL_H_ |
22 | |
23 | #ifdef __cplusplus |
24 | extern "C" |
25 | { |
26 | #endif // __cplusplus |
27 | |
28 | typedef struct _CMI { |
29 | |
30 | struct _CMI * pNext; /* Link to the next entry. */ |
31 | struct _CMI * pPrevious; /* Link to the previous entry. */ |
32 | |
33 | UINT_PTR startBoundary; /* Starting location of the region. */ |
34 | SIZE_T memSize; /* Size of the entire region.. */ |
35 | |
36 | DWORD accessProtection; /* Initial allocation access protection. */ |
37 | DWORD allocationType; /* Initial allocation type. */ |
38 | |
39 | BYTE * pAllocState; /* Individual allocation type tracking for each */ |
40 | /* page in the region. */ |
41 | |
42 | BYTE * pProtectionState; /* Individual allocation type tracking for each */ |
43 | /* page in the region. */ |
44 | |
45 | } CMI, * PCMI; |
46 | |
47 | enum VIRTUAL_CONSTANTS |
48 | { |
49 | /* Allocation type. */ |
50 | VIRTUAL_COMMIT_ALL_BITS = 0xFF, |
51 | VIRTUAL_RESERVE_ALL_BITS = 0x0, |
52 | |
53 | /* Protection Type. */ |
54 | VIRTUAL_READONLY, |
55 | VIRTUAL_READWRITE, |
56 | VIRTUAL_EXECUTE_READWRITE, |
57 | VIRTUAL_NOACCESS, |
58 | VIRTUAL_EXECUTE, |
59 | VIRTUAL_EXECUTE_READ, |
60 | |
61 | VIRTUAL_64KB = 0x10000 |
62 | }; |
63 | |
64 | size_t GetVirtualPageSize(); |
65 | |
66 | /*++ |
67 | Function : |
68 | VIRTUALInitialize |
69 | |
70 | Initialize the critical sections. |
71 | |
72 | Return value: |
73 | TRUE if initialization succeeded |
74 | FALSE otherwise. |
75 | --*/ |
76 | BOOL VIRTUALInitialize(bool initializeExecutableMemoryAllocator); |
77 | |
78 | /*++ |
79 | Function : |
80 | VIRTUALCleanup |
81 | |
82 | Deletes the critical sections. |
83 | |
84 | --*/ |
85 | void VIRTUALCleanup( void ); |
86 | |
87 | #ifdef __cplusplus |
88 | } |
89 | |
90 | /*++ |
91 | Class: |
92 | ExecutableMemoryAllocator |
93 | |
94 | This class implements a virtual memory allocator for JIT'ed code. |
95 | The purpose of this allocator is to opportunistically reserve a chunk of virtual memory |
96 | that is located near the coreclr library (within 2GB range) that can be later used by |
97 | JIT. Having executable memory close to the coreclr library allows JIT to generate more |
98 | efficient code (by avoiding usage of jump stubs) and thus it can significantly improve |
99 | performance of the application. |
100 | |
101 | This allocator is integrated with the VirtualAlloc/Reserve code. If VirtualAlloc has been |
102 | called with the MEM_RESERVE_EXECUTABLE flag then it will first try to obtain the requested size |
103 | of virtual memory from ExecutableMemoryAllocator. If ExecutableMemoryAllocator runs out of |
104 | the reserved memory (or fails to allocate it during initialization) then VirtualAlloc/Reserve code |
105 | will simply fall back to reserving memory using OS APIs. |
106 | |
107 | Notes: |
108 | - the memory allocated by this class is NOT committed by default. It is responsibility |
109 | of the caller to commit the virtual memory before accessing it. |
110 | - in addition, this class does not provide ability to free the reserved memory. The caller |
111 | has full control of the memory it got from this allocator (i.e. the caller becomes |
112 | the owner of the allocated memory), so it is caller's responsibility to free the memory |
113 | if it is no longer needed. |
114 | --*/ |
115 | class ExecutableMemoryAllocator |
116 | { |
117 | public: |
118 | /*++ |
119 | Function: |
120 | Initialize |
121 | |
122 | This function initializes the allocator. It should be called early during process startup |
123 | (when process address space is pretty much empty) in order to have a chance to reserve |
124 | sufficient amount of memory that is close to the coreclr library. |
125 | --*/ |
126 | void Initialize(); |
127 | |
128 | /*++ |
129 | Function: |
130 | AllocateMemory |
131 | |
132 | This function attempts to allocate the requested amount of memory from its reserved virtual |
133 | address space. The function will return null if the allocation request cannot |
134 | be satisfied by the memory that is currently available in the allocator. |
135 | --*/ |
136 | void* AllocateMemory(SIZE_T allocationSize); |
137 | |
138 | /*++ |
139 | Function: |
140 | AllocateMemory |
141 | |
142 | This function attempts to allocate the requested amount of memory from its reserved virtual |
143 | address space, if memory is available within the specified range. The function will return |
144 | null if the allocation request cannot satisfied by the memory that is currently available in |
145 | the allocator. |
146 | --*/ |
147 | void *AllocateMemoryWithinRange(const void *beginAddress, const void *endAddress, SIZE_T allocationSize); |
148 | |
149 | private: |
150 | /*++ |
151 | Function: |
152 | TryReserveInitialMemory |
153 | |
154 | This function is called during initialization. It opportunistically tries to reserve |
155 | a large chunk of virtual memory that can be later used to store JIT'ed code. |
156 | --*/ |
157 | void TryReserveInitialMemory(); |
158 | |
159 | /*++ |
160 | Function: |
161 | GenerateRandomStartOffset |
162 | |
163 | This function returns a random offset (in multiples of the virtual page size) |
164 | at which the allocator should start allocating memory from its reserved memory range. |
165 | --*/ |
166 | int32_t GenerateRandomStartOffset(); |
167 | |
168 | private: |
169 | // There does not seem to be an easy way find the size of a library on Unix. |
170 | // So this constant represents an approximation of the libcoreclr size (on debug build) |
171 | // that can be used to calculate an approximate location of the memory that |
172 | // is in 2GB range from the coreclr library. In addition, having precise size of libcoreclr |
173 | // is not necessary for the calculations. |
174 | static const int32_t CoreClrLibrarySize = 100 * 1024 * 1024; |
175 | |
176 | // This constant represent the max size of the virtual memory that this allocator |
177 | // will try to reserve during initialization. We want all JIT-ed code and the |
178 | // entire libcoreclr to be located in a 2GB range. |
179 | static const int32_t MaxExecutableMemorySize = 0x7FFF0000; |
180 | static const int32_t MaxExecutableMemorySizeNearCoreClr = MaxExecutableMemorySize - CoreClrLibrarySize; |
181 | |
182 | // Start address of the reserved virtual address space |
183 | void* m_startAddress; |
184 | |
185 | // Next available address in the reserved address space |
186 | void* m_nextFreeAddress; |
187 | |
188 | // Total size of the virtual memory that the allocator has been able to |
189 | // reserve during its initialization. |
190 | int32_t m_totalSizeOfReservedMemory; |
191 | |
192 | // Remaining size of the reserved virtual memory that can be used to satisfy allocation requests. |
193 | int32_t m_remainingReservedMemory; |
194 | }; |
195 | |
196 | #endif // __cplusplus |
197 | |
198 | /*++ |
199 | Function : |
200 | ReserveMemoryFromExecutableAllocator |
201 | |
202 | This function is used to reserve a region of virual memory (not commited) |
203 | that is located close to the coreclr library. The memory comes from the virtual |
204 | address range that is managed by ExecutableMemoryAllocator. |
205 | --*/ |
206 | void* ReserveMemoryFromExecutableAllocator(CorUnix::CPalThread* pthrCurrent, SIZE_T allocationSize); |
207 | |
208 | #endif /* _PAL_VIRTUAL_H_ */ |
209 | |
210 | |
211 | |
212 | |
213 | |
214 | |
215 | |
216 | |