summaryrefslogtreecommitdiff
path: root/src/pal/src/include/pal/virtual.h
blob: 31d225fc0442844a7a516f4a09f52913a4b02e8f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

/*++



Module Name:

    include/pal/virtual.h

Abstract:
    Header file for virtual memory management.



--*/

#ifndef _PAL_VIRTUAL_H_
#define _PAL_VIRTUAL_H_

#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus

typedef struct _CMI {

    struct _CMI * pNext;        /* Link to the next entry. */
    struct _CMI * pPrevious;    /* Link to the previous entry. */

    UINT_PTR startBoundary;     /* Starting location of the region. */
    SIZE_T   memSize;           /* Size of the entire region.. */

    DWORD  accessProtection;    /* Initial allocation access protection. */
    DWORD  allocationType;      /* Initial allocation type. */

    BYTE * pAllocState;         /* Individual allocation type tracking for each */
                                /* page in the region. */

    BYTE * pProtectionState;    /* Individual allocation type tracking for each */
                                /* page in the region. */

} CMI, * PCMI;

enum VIRTUAL_CONSTANTS
{
    /* Allocation type. */
    VIRTUAL_COMMIT_ALL_BITS     = 0xFF,
    VIRTUAL_RESERVE_ALL_BITS    = 0x0,
    
    /* Protection Type. */
    VIRTUAL_READONLY,
    VIRTUAL_READWRITE,
    VIRTUAL_EXECUTE_READWRITE,
    VIRTUAL_NOACCESS,
    VIRTUAL_EXECUTE,
    VIRTUAL_EXECUTE_READ,
    
    VIRTUAL_PAGE_SIZE       = 0x1000,
    VIRTUAL_PAGE_MASK       = VIRTUAL_PAGE_SIZE - 1,
    BOUNDARY_64K    = 0xffff
};

/*++
Function :
    VIRTUALInitialize

    Initialize the critical sections.

Return value:
    TRUE  if initialization succeeded
    FALSE otherwise.        
--*/
BOOL VIRTUALInitialize(bool initializeExecutableMemoryAllocator);

/*++
Function :
    VIRTUALCleanup

    Deletes the critical sections.

--*/
void VIRTUALCleanup( void );

#ifdef __cplusplus
}

/*++
Class:
    ExecutableMemoryAllocator

    This class implements a virtual memory allocator for JIT'ed code.
    The purpose of this allocator is to opportunistically reserve a chunk of virtual memory
    that is located near the coreclr library (within 2GB range) that can be later used by
    JIT. Having executable memory close to the coreclr library allows JIT to generate more
    efficient code (by avoiding usage of jump stubs) and thus it can significantly improve
    performance of the application.

    This allocator is integrated with the VirtualAlloc/Reserve code. If VirtualAlloc has been
    called with the MEM_RESERVE_EXECUTABLE flag then it will first try to obtain the requested size
    of virtual memory from ExecutableMemoryAllocator. If ExecutableMemoryAllocator runs out of
    the reserved memory (or fails to allocate it during initialization) then VirtualAlloc/Reserve code
    will simply fall back to reserving memory using OS APIs.

    Notes:
        - the memory allocated by this class is NOT committed by default. It is responsibility
          of the caller to commit the virtual memory before accessing it.
        - in addition, this class does not provide ability to free the reserved memory. The caller
          has full control of the memory it got from this allocator (i.e. the caller becomes
          the owner of the allocated memory), so it is caller's responsibility to free the memory
          if it is no longer needed.
--*/
class ExecutableMemoryAllocator
{
public:
    /*++
    Function:
        Initialize

        This function initializes the allocator. It should be called early during process startup
        (when process address space is pretty much empty) in order to have a chance to reserve
        sufficient amount of memory that is close to the coreclr library.
    --*/
    void Initialize();

    /*++
    Function:
        AllocateMemory

        This function attempts to allocate the requested amount of memory from its reserved virtual
        address space. The function will return NULL if the allocation request cannot
        be satisfied by the memory that is currently available in the allocator.
    --*/
    void* AllocateMemory(SIZE_T allocationSize);

private:
    /*++
    Function:
        TryReserveInitialMemory

        This function is called during initialization. It opportunistically tries to reserve
        a large chunk of virtual memory that can be later used to store JIT'ed code.
    --*/
    void TryReserveInitialMemory();

    /*++
    Function:
        GenerateRandomStartOffset

        This function returns a random offset (in multiples of the virtual page size)
        at which the allocator should start allocating memory from its reserved memory range.
    --*/
    int32_t GenerateRandomStartOffset();

private:
    // There does not seem to be an easy way find the size of a library on Unix.
    // So this constant represents an approximation of the libcoreclr size (on debug build)
    // that can be used to calculate an approximate location of the memory that
    // is in 2GB range from the coreclr library. In addition, having precise size of libcoreclr
    // is not necessary for the calculations.
    const int32_t CoreClrLibrarySize = 100 * 1024 * 1024;

    // This constant represent the max size of the virtual memory that this allocator
    // will try to reserve during initialization. We want all JIT-ed code and the
    // entire libcoreclr to be located in a 2GB range.
    const int32_t MaxExecutableMemorySize = 0x7FFF0000 - CoreClrLibrarySize;

    // Start address of the reserved virtual address space
    void* m_startAddress;

    // Next available address in the reserved address space
    void* m_nextFreeAddress;

    // Total size of the virtual memory that the allocator has been able to
    // reserve during its initialization.
    int32_t m_totalSizeOfReservedMemory;

    // Remaining size of the reserved virtual memory that can be used to satisfy allocation requests.
    int32_t m_remainingReservedMemory;
};

#endif // __cplusplus

/*++
Function :
    ReserveMemoryFromExecutableAllocator

    This function is used to reserve a region of virual memory (not commited)
    that is located close to the coreclr library. The memory comes from the virtual
    address range that is managed by ExecutableMemoryAllocator.
--*/
void* ReserveMemoryFromExecutableAllocator(CorUnix::CPalThread* pthrCurrent, SIZE_T allocationSize);

#endif /* _PAL_VIRTUAL_H_ */