summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Reflection/LoaderAllocator.cs
blob: 7c6c6bd0e8e81bc64abfe2aa7015504a9011f706 (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
// 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.

// 

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Security;
using System.Collections.Generic;


namespace System.Reflection
{
    //
    // We can destroy the unmanaged part of collectible type only after the managed part is definitely gone and thus
    // nobody can call/allocate/reference anything related to the collectible assembly anymore. A call to finalizer 
    // alone does not guarantee that the managed part is gone. A malicious code can keep a reference to some object
    // in a way that that survives finalization, or we can be running during shutdown where everything is finalized.
    //
    // The unmanaged LoaderAllocator keeps a reference to the managed LoaderAllocator in long weak handle. If the long 
    // weak handle is null, we can be sure that the managed part of the LoaderAllocator is definitely gone and that it 
    // is safe to destroy the unmanaged part. Unfortunately, we can not perform the above check in a finalizer on the
    // LoaderAllocator, but it can be performed on a helper object. 
    //
    // The finalization does not have to be done using CriticalFinalizerObject. We have to go over all LoaderAllocators 
    // during AppDomain shutdown anyway to avoid leaks e.g. if somebody stores reference to LoaderAllocator in a static.
    //
    internal sealed class LoaderAllocatorScout
    {
        // This field is set by the VM to atomically transfer the ownership to the managed loader allocator
        internal IntPtr m_nativeLoaderAllocator;

        [SuppressUnmanagedCodeSecurity]
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
        private static extern bool Destroy(IntPtr nativeLoaderAllocator);

        ~LoaderAllocatorScout()
        {
            if (m_nativeLoaderAllocator.IsNull())
                return;

            // Assemblies and LoaderAllocators will be cleaned up during AppDomain shutdown in
            // unmanaged code
            // So it is ok to skip reregistration and cleanup for finalization during appdomain shutdown.
            // We also avoid early finalization of LoaderAllocatorScout due to AD unload when the object was inside DelayedFinalizationList.
            if (!Environment.HasShutdownStarted &&
                !AppDomain.CurrentDomain.IsFinalizingForUnload())
            {

                // Destroy returns false if the managed LoaderAllocator is still alive.
                if (!Destroy(m_nativeLoaderAllocator))
                {
                    // Somebody might have been holding a reference on us via weak handle.
                    // We will keep trying. It will be hopefully released eventually.
                    GC.ReRegisterForFinalize(this);
                }
            }
        }
    }

    internal sealed class LoaderAllocator
    {
        LoaderAllocator()
        {
            m_slots = new object [5];
            // m_slotsUsed = 0;

            m_scout = new LoaderAllocatorScout();
        }

#pragma warning disable 169
#pragma warning disable 414
        LoaderAllocatorScout m_scout;
        object [] m_slots;
        internal CerHashtable<RuntimeMethodInfo, RuntimeMethodInfo> m_methodInstantiations;
        int m_slotsUsed;
#pragma warning restore 414
#pragma warning restore 169
    }
}