summaryrefslogtreecommitdiff
path: root/src/md/compiler/regmeta_vm.cpp
blob: 38ca308e019d86496547c2eb4bd1a3290d526dcf (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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
// 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.

    
//*****************************************************************************

// 
// RegMeta.cpp
//
// Implementation for meta data public interface methods for full version.
//
//*****************************************************************************
#include "stdafx.h"
#include "regmeta.h"
#include "metadata.h"
#include "corerror.h"
#include "mdutil.h"
#include "rwutil.h"
#include "mdlog.h"
#include "importhelper.h"
#include "filtermanager.h"
#include "mdperf.h"
#include "switches.h"
#include "posterror.h"
#include "stgio.h"
#include "sstring.h"

#include <metamodelrw.h>




#define DEFINE_CUSTOM_NODUPCHECK    1
#define DEFINE_CUSTOM_DUPCHECK      2
#define SET_CUSTOM                  3

#if defined(_DEBUG) && defined(_TRACE_REMAPS)
#define LOGGING
#endif
#include <log.h>

#ifdef _MSC_VER
#pragma warning(disable: 4102)
#endif

//*****************************************************************************
// Call this after initialization is complete.
//*****************************************************************************
HRESULT RegMeta::AddToCache()
{
#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
    HRESULT hr = S_OK;

    // The ref count must be > 0 before the module is published, else another
    //  thread could find, use, and release the module, causing it to be deleted
    //  before this thread gets a chance to addref.
    _ASSERTE(GetRefCount() > 0);
    // add this RegMeta to the loaded module list.
    m_bCached = true;
    IfFailGo(LOADEDMODULES::AddModuleToLoadedList(this));
ErrExit:
    if (FAILED(hr))
    {
        _ASSERTE(!LOADEDMODULES::IsEntryInList(this));
        m_bCached = false;
    }
    return hr;
#else //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
    return S_OK;
#endif //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
} // RegMeta::AddToCache


//*****************************************************************************
// Search the cached RegMetas for a given scope.
//*****************************************************************************
HRESULT RegMeta::FindCachedReadOnlyEntry(
    LPCWSTR     szName,                 // Name of the desired file.
    DWORD       dwOpenFlags,            // Flags the new file is opened with.
    RegMeta     **ppMeta)               // Put found RegMeta here.
{
#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
    return LOADEDMODULES::FindCachedReadOnlyEntry(szName, dwOpenFlags, ppMeta);
#else //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
    // No cache support in standalone version.
    *ppMeta = NULL;
    return S_FALSE;
#endif //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
} // RegMeta::FindCachedReadOnlyEntry


#ifdef FEATURE_METADATA_EMIT_ALL

//*****************************************************************************
// Helper function to startup the EE
// 
// Notes:
//    This is called by code:RegMeta.DefineSecurityAttributeSet.
//*****************************************************************************
HRESULT RegMeta::StartupEE()
{
    UNREACHABLE_MSG_RET("About to CoCreateInstance!  This code should not be "
                        "reachable or needs to be reimplemented for CoreCLR!");
}

#endif //FEATURE_METADATA_EMIT_ALL

#ifdef FEATURE_METADATA_EMIT

//*****************************************************************************
// Persist a set of security custom attributes into a set of permission set
// blobs on the same class or method.
// 
// Notes:
//    Only in the full version because this is an emit operation.
//*****************************************************************************
HRESULT RegMeta::DefineSecurityAttributeSet(// Return code.
    mdToken     tkObj,                  // [IN] Class or method requiring security attributes.
    COR_SECATTR rSecAttrs[],            // [IN] Array of security attribute descriptions.
    ULONG       cSecAttrs,              // [IN] Count of elements in above array.
    ULONG       *pulErrorAttr)          // [OUT] On error, index of attribute causing problem.
{
#ifdef FEATURE_METADATA_EMIT_ALL
    HRESULT hr = S_OK;

    BEGIN_ENTRYPOINT_NOTHROW;

    NewArrayHolder <CORSEC_ATTRSET> rAttrSets;
    DWORD           i;
    mdPermission    ps;
    DWORD           dwAction;
    bool fProcessDeclarativeSecurityAtRuntime;

    LOG((LOGMD, "RegMeta::DefineSecurityAttributeSet(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
         tkObj, rSecAttrs, cSecAttrs, pulErrorAttr));
    START_MD_PERF();
    LOCKWRITE();
    
    IfFailGo(m_pStgdb->m_MiniMd.PreUpdate());
    
    rAttrSets = new (nothrow) CORSEC_ATTRSET[dclMaximumValue + 1];
    if (rAttrSets == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto ErrExit;
    }

    memset(rAttrSets, 0, sizeof(CORSEC_ATTRSET) * (dclMaximumValue + 1));

    // Initialize error index to indicate a general error.
    if (pulErrorAttr)
        *pulErrorAttr = cSecAttrs;

    fProcessDeclarativeSecurityAtRuntime = true;

    // See if we should default to old v1.0/v1.1 serialization behavior
    if (m_OptionValue.m_MetadataVersion < MDVersion2)
        fProcessDeclarativeSecurityAtRuntime = false;

    // Startup the EE just once, no matter how many times we're called (this is
    // better on performance and the EE falls over if we try a start-stop-start
    // cycle anyway).
    if (!m_fStartedEE && !fProcessDeclarativeSecurityAtRuntime)
    {
        IfFailGo(StartupEE());
    }

    // Group the security attributes by SecurityAction (thus creating an array of CORSEC_PERM's)
    IfFailGo(GroupSecurityAttributesByAction(/*OUT*/rAttrSets, rSecAttrs, cSecAttrs, tkObj, pulErrorAttr, &m_pStgdb->m_MiniMd, NULL));
    
    // Put appropriate data in the metadata
    for (i = 0; i <= dclMaximumValue; i++) 
    {
        NewArrayHolder <BYTE>    pbBlob(NULL);
        NewArrayHolder <BYTE>    pbNonCasBlob(NULL);
        DWORD              cbBlob = 0;
        DWORD              cbNonCasBlob = 0;

        rAttrSets[i].pImport = this;
        rAttrSets[i].pAppDomain = m_pAppDomain;
        if (rAttrSets[i].dwAttrCount == 0)
            continue;
        if (pulErrorAttr)
            *pulErrorAttr = i;

        if(fProcessDeclarativeSecurityAtRuntime)
        {
            // Put a serialized CORSEC_ATTRSET in the metadata
            SIZE_T cbAttrSet = 0;
            IfFailGo(AttributeSetToBlob(&rAttrSets[i], NULL, &cbAttrSet, this, i)); // count size required for buffer
            if (!FitsIn<DWORD>(cbAttrSet))
            {
                hr = COR_E_OVERFLOW;
                goto ErrExit;
            }
            cbBlob = static_cast<DWORD>(cbAttrSet);

            pbBlob = new (nothrow) BYTE[cbBlob]; // allocate buffer
            if (pbBlob == NULL)
            {
                hr = E_OUTOFMEMORY;
                goto ErrExit;
            }

            IfFailGo(AttributeSetToBlob(&rAttrSets[i], pbBlob, NULL, this, i)); // serialize into the buffer
            IfFailGo(_DefinePermissionSet(rAttrSets[i].tkObj, rAttrSets[i].dwAction, pbBlob, cbBlob, &ps)); // put it in metadata
        }
        else
        {
            // Now translate the sets of security attributes into a real permission
            // set and convert this to a serialized Xml blob. We may possibly end up
            // with two sets as the result of splitting CAS and non-CAS permissions
            // into separate sets.
            hr = TranslateSecurityAttributes(&rAttrSets[i], &pbBlob, &cbBlob, &pbNonCasBlob, &cbNonCasBlob, pulErrorAttr);
            IfFailGo(hr);

            // Persist the permission set blob into the metadata. For empty CAS
            // blobs this is only done if the corresponding non-CAS blob is empty
            if (cbBlob || !cbNonCasBlob)
                IfFailGo(_DefinePermissionSet(rAttrSets[i].tkObj, rAttrSets[i].dwAction, pbBlob, cbBlob, &ps));
            
            if (pbNonCasBlob)
            {
                // Map the SecurityAction to a special non-CAS action so this
                // blob will have its own entry in the metadata
                switch (rAttrSets[i].dwAction)
                {
                case dclDemand:
                    dwAction = dclNonCasDemand;
                    break;
                case dclLinktimeCheck:
                    dwAction = dclNonCasLinkDemand;
                    break;
                case dclInheritanceCheck:
                    dwAction = dclNonCasInheritance;
                    break;
                default:
                    PostError(CORSECATTR_E_BAD_NONCAS);
                    IfFailGo(CORSECATTR_E_BAD_NONCAS);
                }

                // Persist to metadata
                IfFailGo(_DefinePermissionSet(rAttrSets[i].tkObj,
                                              dwAction,
                                              pbNonCasBlob,
                                              cbNonCasBlob,
                                              &ps));
            }
        }
    }

ErrExit:
    STOP_MD_PERF(DefineSecurityAttributeSet);

    END_ENTRYPOINT_NOTHROW;
    
    return (hr);
#else //!FEATURE_METADATA_EMIT_ALL
    return E_NOTIMPL;
#endif //!FEATURE_METADATA_EMIT_ALL
} // RegMeta::DefineSecurityAttributeSet

#endif //FEATURE_METADATA_EMIT


//*****************************************************************************
// Implementation of IMetaDataImport::ResolveTypeRef to resolve a typeref across scopes. 
// 
// Arguments:
//    tr - typeref within this scope to resolve
//    riid - interface on ppIScope to support
//    ppIScope - out-parameter to get metadata scope for typedef (*ptd)
//    ptd - out-parameter to get typedef that the ref resolves to.
// 
// Notes:
// TypeDefs define a type within a scope. TypeRefs refer to type-defs in other scopes
// and allow you to import a type from another scope. This function attempts to determine
// which type-def a type-ref points to.
// 
// This resolve (type-ref, this cope) --> (type-def=*ptd, other scope=*ppIScope)
// 
// However, this resolution requires knowing what modules have been loaded, which is not decided
// until runtime via loader / fusion policy. Thus this interface can't possibly be correct since
// it doesn't have that knowledge. Furthermore, when inspecting metadata from another process
// (such as a debugger inspecting the debuggee's metadata), this API can be truly misleading.
// 
// This API usage should be avoided.
// 
//*****************************************************************************
STDMETHODIMP 
RegMeta::ResolveTypeRef(
    mdTypeRef   tr, 
    REFIID      riid, 
    IUnknown ** ppIScope, 
    mdTypeDef * ptd)
{
#ifdef FEATURE_METADATA_IN_VM
    HRESULT hr;

    BEGIN_ENTRYPOINT_NOTHROW;

    TypeRefRec * pTypeRefRec;
    WCHAR        wzNameSpace[_MAX_PATH];
    CMiniMdRW *  pMiniMd = NULL;
    WCHAR rcModule[_MAX_PATH];

    LOG((LOGMD, "{%08x} RegMeta::ResolveTypeRef(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", 
        this, tr, riid, ppIScope, ptd));

    START_MD_PERF();
    LOCKREAD();

    pMiniMd = &(m_pStgdb->m_MiniMd);

    _ASSERTE((ppIScope != NULL) && (ptd != NULL));

    // Init the output values.
    *ppIScope = NULL;
    *ptd = 0;

    if (IsNilToken(tr))
    {
        if (ptd != NULL) 
        {
            *ptd = mdTypeDefNil;
        }

        if (ppIScope != NULL)
        {
            *ppIScope = NULL;
        }
        
        STOP_MD_PERF(ResolveTypeRef);
        hr = E_INVALIDARG;
        goto ErrExit;
    }

    if (TypeFromToken(tr) == mdtTypeDef)
    {
        // Shortcut when we receive a TypeDef token
        *ptd = tr;
        STOP_MD_PERF(ResolveTypeRef);
        hr = this->QueryInterface(riid, (void **)ppIScope);
        goto ErrExit;
    }

    // Get the class ref row.
    _ASSERTE(TypeFromToken(tr) == mdtTypeRef);

    IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tr), &pTypeRefRec));
    IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, wzNameSpace, lengthof(wzNameSpace), NULL));
    if (hr != NOERROR)
    {
        _ASSERTE(hr == CLDB_S_TRUNCATION);
        // Truncate the namespace string
        wzNameSpace[lengthof(wzNameSpace) - 1] = 0;
    }
    
    //***********************
    // before we go off to CORPATH, check the loaded modules!
    //***********************
    if (LOADEDMODULES::ResolveTypeRefWithLoadedModules(
        tr, 
        this, 
        pMiniMd, 
        riid, 
        ppIScope, 
        ptd) == NOERROR)
    {
        // Done!! We found one match among the loaded modules.
        goto ErrExit;
    }

    IfFailGo(META_E_CANNOTRESOLVETYPEREF);

ErrExit:
    STOP_MD_PERF(ResolveTypeRef);
    END_ENTRYPOINT_NOTHROW;
    
    return hr;
#else //!FEATURE_METADATA_IN_VM
    return E_NOTIMPL;
#endif //!FEATURE_METADATA_IN_VM
} // RegMeta::ResolveTypeRef



// Full version handles metadata caching, which Release() needs to coordinate with. 
// Thus Release() is in a satellite lib.
ULONG RegMeta::Release()
{
    // This is called during cleanup.  We can not fail this call by probing.
    // As long as we make sure the cleanup does not use too much space through 
    // BEGIN_CLEANUP_ENTRYPOINT, we are OK.
    CONTRACT_VIOLATION (SOToleranceViolation);
    BEGIN_CLEANUP_ENTRYPOINT;

#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
    _ASSERTE(!m_bCached || LOADEDMODULES::IsEntryInList(this));
#else
    _ASSERTE(!m_bCached);
#endif //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
    BOOL  bCached = m_bCached;
    ULONG cRef = InterlockedDecrement(&m_cRef);
    // NOTE: 'this' may be unsafe after this point, if the module is cached, and
    //  another thread finds the module in the cache, releases it, and deletes it
    //  before we get around to deleting it. (That's why we must make a local copy
    //  of m_bCached.)
    // If no references left...
    if (cRef == 0)
    {
        if (!bCached)
        {   // If the module is not (was not) cached, no other thread can have
            //  discovered the module, so this thread can now safely delete it.
            delete this;
        }
#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
        else if (LOADEDMODULES::RemoveModuleFromLoadedList(this))
        {   // If the module was cached, RemoveModuleFromLoadedList() will try to
            //  safely un-publish the module, and if it succeeds, no other thread
            //  has (or will) discover the module, so this thread can delete it.
            m_bCached = false;
            delete this;
        }
#endif //!FEATURE_METADATA_IN_VM && !FEATURE_METADATA_STANDALONE_WINRT
    }
    END_CLEANUP_ENTRYPOINT
    
    return cRef;
} // RegMeta::Release