summaryrefslogtreecommitdiff
path: root/src/vm/gcheaputilities.cpp
blob: 90836a0fd218059c7197970345d3a876fb589aac (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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// 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.

#include "common.h"
#include "gcheaputilities.h"
#include "gcenv.ee.h"
#include "appdomain.hpp"


// These globals are variables used within the GC and maintained
// by the EE for use in write barriers. It is the responsibility
// of the GC to communicate updates to these globals to the EE through
// GCToEEInterface::StompWriteBarrierResize and GCToEEInterface::StompWriteBarrierEphemeral.
GPTR_IMPL_INIT(uint32_t, g_card_table,      nullptr);
GPTR_IMPL_INIT(uint8_t,  g_lowest_address,  nullptr);
GPTR_IMPL_INIT(uint8_t,  g_highest_address, nullptr);
GVAL_IMPL_INIT(GCHeapType, g_heap_type,     GC_HEAP_INVALID);
uint8_t* g_ephemeral_low  = (uint8_t*)1;
uint8_t* g_ephemeral_high = (uint8_t*)~0;

#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
uint32_t* g_card_bundle_table = nullptr;
#endif

// This is the global GC heap, maintained by the VM.
GPTR_IMPL(IGCHeap, g_pGCHeap);

GcDacVars g_gc_dac_vars;
GPTR_IMPL(GcDacVars, g_gcDacGlobals);

#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP

uint8_t* g_sw_ww_table = nullptr;
bool g_sw_ww_enabled_for_gc_heap = false;

#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP

gc_alloc_context g_global_alloc_context = {};

enum GC_LOAD_STATUS {
    GC_LOAD_STATUS_BEFORE_START,
    GC_LOAD_STATUS_START,
    GC_LOAD_STATUS_DONE_LOAD,
    GC_LOAD_STATUS_GET_VERSIONINFO,
    GC_LOAD_STATUS_CALL_VERSIONINFO,
    GC_LOAD_STATUS_DONE_VERSION_CHECK,
    GC_LOAD_STATUS_GET_INITIALIZE,
    GC_LOAD_STATUS_LOAD_COMPLETE
};

// Load status of the GC. If GC loading fails, the value of this
// global indicates where the failure occured.
GC_LOAD_STATUS g_gc_load_status = GC_LOAD_STATUS_BEFORE_START;

// The version of the GC that we have loaded.
VersionInfo g_gc_version_info;

// GC entrypoints for the the linked-in GC. These symbols are invoked
// directly if we are not using a standalone GC.
extern "C" void GC_VersionInfo(/* Out */ VersionInfo* info);
extern "C" HRESULT GC_Initialize(
    /* In  */ IGCToCLR* clrToGC,
    /* Out */ IGCHeap** gcHeap,
    /* Out */ IGCHandleManager** gcHandleManager,
    /* Out */ GcDacVars* gcDacVars
);

#ifndef DACCESS_COMPILE

namespace
{

// Loads and initializes a standalone GC, given the path to the GC
// that we should load. Returns S_OK on success and the failed HRESULT
// on failure.
//
// See Documentation/design-docs/standalone-gc-loading.md for details
// on the loading protocol in use here.
HRESULT LoadAndInitializeGC(LPWSTR standaloneGcLocation)
{
    LIMITED_METHOD_CONTRACT;

#ifndef FEATURE_STANDALONE_GC
    LOG((LF_GC, LL_FATALERROR, "EE not built with the ability to load standalone GCs"));
    return E_FAIL;
#else
    LOG((LF_GC, LL_INFO100, "Loading standalone GC from path %S\n", standaloneGcLocation));
    HMODULE hMod = CLRLoadLibrary(standaloneGcLocation);
    if (!hMod)
    {
        HRESULT err = GetLastError();
        LOG((LF_GC, LL_FATALERROR, "Load of %S failed\n", standaloneGcLocation));
        return err;
    }

    // a standalone GC dispatches virtually on GCToEEInterface, so we must instantiate
    // a class for the GC to use.
    IGCToCLR* gcToClr = new (nothrow) standalone::GCToEEInterface();
    if (!gcToClr)
    {
        return E_OUTOFMEMORY;
    }

    g_gc_load_status = GC_LOAD_STATUS_DONE_LOAD;
    GC_VersionInfoFunction versionInfo = (GC_VersionInfoFunction)GetProcAddress(hMod, "GC_VersionInfo");
    if (!versionInfo)
    {
        HRESULT err = GetLastError();
        LOG((LF_GC, LL_FATALERROR, "Load of `GC_VersionInfo` from standalone GC failed\n"));
        return err;
    }

    g_gc_load_status = GC_LOAD_STATUS_GET_VERSIONINFO;
    versionInfo(&g_gc_version_info);
    g_gc_load_status = GC_LOAD_STATUS_CALL_VERSIONINFO;

    if (g_gc_version_info.MajorVersion != GC_INTERFACE_MAJOR_VERSION)
    {
        LOG((LF_GC, LL_FATALERROR, "Loaded GC has incompatible major version number (expected %d, got %d)\n",
            GC_INTERFACE_MAJOR_VERSION, g_gc_version_info.MajorVersion));
        return E_FAIL;
    }

    if (g_gc_version_info.MinorVersion < GC_INTERFACE_MINOR_VERSION)
    {
        LOG((LF_GC, LL_INFO100, "Loaded GC has lower minor version number (%d) than EE was compiled against (%d)\n",
            g_gc_version_info.MinorVersion, GC_INTERFACE_MINOR_VERSION));
    }

    LOG((LF_GC, LL_INFO100, "Loaded GC identifying itself with name `%s`\n", g_gc_version_info.Name));
    g_gc_load_status = GC_LOAD_STATUS_DONE_VERSION_CHECK;
    GC_InitializeFunction initFunc = (GC_InitializeFunction)GetProcAddress(hMod, "GC_Initialize");
    if (!initFunc)
    {
        HRESULT err = GetLastError();
        LOG((LF_GC, LL_FATALERROR, "Load of `GC_Initialize` from standalone GC failed\n"));
        return err;
    }

    g_gc_load_status = GC_LOAD_STATUS_GET_INITIALIZE;
    IGCHeap* heap;
    IGCHandleManager* manager;
    HRESULT initResult = initFunc(gcToClr, &heap, &manager, &g_gc_dac_vars);
    if (initResult == S_OK)
    {
        g_pGCHeap = heap;
        g_pGCHandleManager = manager;
        g_gcDacGlobals = &g_gc_dac_vars;
        g_gc_load_status = GC_LOAD_STATUS_LOAD_COMPLETE;
        LOG((LF_GC, LL_INFO100, "GC load successful\n"));
    }
    else
    {
        LOG((LF_GC, LL_FATALERROR, "GC initialization failed with HR = 0x%X\n", initResult));
    }

    return initResult;
#endif // FEATURE_STANDALONE_GC
}

// Initializes a non-standalone GC. The protocol for initializing a non-standalone GC
// is similar to loading a standalone one, except that the GC_VersionInfo and
// GC_Initialize symbols are linked to directory and thus don't need to be loaded.
//
// The major and minor versions are still checked in debug builds - it must be the case
// that the GC and EE agree on a shared version number because they are built from
// the same sources.
HRESULT InitializeDefaultGC()
{
    LIMITED_METHOD_CONTRACT;

    LOG((LF_GC, LL_INFO100, "Standalone GC location not provided, using provided GC\n"));

    g_gc_load_status = GC_LOAD_STATUS_DONE_LOAD;
    VersionInfo info;
    GC_VersionInfo(&g_gc_version_info);
    g_gc_load_status = GC_LOAD_STATUS_CALL_VERSIONINFO;

    // the default GC builds with the rest of the EE. By definition, it must have been
    // built with the same interface version.
    assert(g_gc_version_info.MajorVersion == GC_INTERFACE_MAJOR_VERSION);
    assert(g_gc_version_info.MinorVersion == GC_INTERFACE_MINOR_VERSION);
    g_gc_load_status = GC_LOAD_STATUS_DONE_VERSION_CHECK;

    IGCHeap* heap;
    IGCHandleManager* manager;
    HRESULT initResult = GC_Initialize(nullptr, &heap, &manager, &g_gc_dac_vars);
    if (initResult == S_OK)
    {
        g_pGCHeap = heap;
        g_pGCHandleManager = manager;
        g_gcDacGlobals = &g_gc_dac_vars;
        g_gc_load_status = GC_LOAD_STATUS_LOAD_COMPLETE;
        LOG((LF_GC, LL_INFO100, "GC load successful\n"));
    }
    else
    {
        LOG((LF_GC, LL_FATALERROR, "GC initialization failed with HR = 0x%X\n", initResult));
    }


    return initResult;
}

} // anonymous namespace

// Loads (if necessary) and initializes the GC. If using a standalone GC,
// it loads the library containing it and dynamically loads the GC entry point.
// If using a non-standalone GC, it invokes the GC entry point directly.
HRESULT GCHeapUtilities::LoadAndInitialize()
{
    LIMITED_METHOD_CONTRACT;

    // we should only call this once on startup. Attempting to load a GC
    // twice is an error.
    assert(g_pGCHeap == nullptr);

    // we should not have attempted to load a GC already. Attempting a
    // load after the first load already failed is an error.
    assert(g_gc_load_status == GC_LOAD_STATUS_BEFORE_START);
    g_gc_load_status = GC_LOAD_STATUS_START;

    LPWSTR standaloneGcLocation = nullptr;
    CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCName, &standaloneGcLocation);
    if (!standaloneGcLocation)
    {
        return InitializeDefaultGC();
    }
    else
    {
        return LoadAndInitializeGC(standaloneGcLocation);
    }
}

#endif // DACCESS_COMPILE