summaryrefslogtreecommitdiff
path: root/src/vm/amd64/gmsamd64.cpp
blob: d595db0c916b2cbd136e5c0829e487d16f760bca (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
// 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.

/**************************************************************/
/*                       gmsAMD64.cpp                         */
/**************************************************************/

#include "common.h"
#include "gmscpu.h"

void LazyMachState::unwindLazyState(LazyMachState* baseState,
                                    MachState* unwoundState,
                                    DWORD threadId,
                                    int funCallDepth /* = 1 */,
                                    HostCallPreference hostCallPreference /* = (HostCallPreference)(-1) */)
{
    CONTRACTL 
    {
        NOTHROW;
        GC_NOTRIGGER;
        SO_TOLERANT;
        SUPPORTS_DAC;
    }
    CONTRACTL_END;

    CONTEXT                         ctx;
    KNONVOLATILE_CONTEXT_POINTERS   nonVolRegPtrs;

    ctx.Rip = baseState->m_CaptureRip;
    ctx.Rsp = baseState->m_CaptureRsp + 8; // +8 for return addr pushed before calling LazyMachStateCaptureState
    
#define CALLEE_SAVED_REGISTER(regname) ctx.regname = unwoundState->m_Capture.regname = baseState->m_Capture.regname;
    ENUM_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER

#if !defined(DACCESS_COMPILE)

    // For DAC, if we get here, it means that the LazyMachState is uninitialized and we have to unwind it.
    // The API we use to unwind in DAC is StackWalk64(), which does not support the context pointers.
#define CALLEE_SAVED_REGISTER(regname) nonVolRegPtrs.regname = (PDWORD64)&unwoundState->m_Capture.regname;
    ENUM_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER

#endif // !DACCESS_COMPILE

    LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    LazyMachState::unwindLazyState(ip:%p,sp:%p)\n", baseState->m_CaptureRip, baseState->m_CaptureRsp));

    PCODE pvControlPc;
    
    do 
    {

#ifndef FEATURE_PAL
        pvControlPc = Thread::VirtualUnwindCallFrame(&ctx, &nonVolRegPtrs);
#else // !FEATURE_PAL
        
#if defined(DACCESS_COMPILE)
        HRESULT hr = DacVirtualUnwind(threadId, &ctx, &nonVolRegPtrs);
        if (FAILED(hr))
        {
            DacError(hr);
        }
#else
        BOOL success = PAL_VirtualUnwind(&ctx, &nonVolRegPtrs);
        if (!success)
        {
            _ASSERTE(!"unwindLazyState: Unwinding failed");
            EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
        }
#endif  // DACCESS_COMPILE    

        pvControlPc = GetIP(&ctx);
#endif // !FEATURE_PAL

        if (funCallDepth > 0)
        {
            --funCallDepth;
            if (funCallDepth == 0)
                break;
        }
        else
        {
            // Determine  whether given IP resides in JITted code. (It returns nonzero in that case.) 
            // Use it now to see if we've unwound to managed code yet.
            BOOL fFailedReaderLock = FALSE;
            BOOL fIsManagedCode = ExecutionManager::IsManagedCode(pvControlPc, hostCallPreference, &fFailedReaderLock);            
            if (fFailedReaderLock)
            {
                // We don't know if we would have been able to find a JIT
                // manager, because we couldn't enter the reader lock without
                // yielding (and our caller doesn't want us to yield).  So abort
                // now.
                
                // Invalidate the lazyState we're returning, so the caller knows
                // we aborted before we could fully unwind
                unwoundState->_pRetAddr = NULL;                
                return;
            }

            if (fIsManagedCode)
                break;
        }    
    }
    while(TRUE);

    //
    // Update unwoundState so that HelperMethodFrameRestoreState knows which
    // registers have been potentially modified.  
    //

    unwoundState->m_Rip = ctx.Rip;
    unwoundState->m_Rsp = ctx.Rsp;

    // For DAC, the return value of this function may be used after unwoundState goes out of scope. so we cannot do
    // "unwoundState->_pRetAddr = PTR_TADDR(&unwoundState->m_Rip)".
    unwoundState->_pRetAddr = PTR_TADDR(unwoundState->m_Rsp - 8);

#ifdef FEATURE_PAL
#define CALLEE_SAVED_REGISTER(regname) unwoundState->m_Unwound.regname = ctx.regname;
    ENUM_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER
#endif

#if defined(DACCESS_COMPILE)

    // For DAC, we have to update the registers directly, since we don't have context pointers.
#define CALLEE_SAVED_REGISTER(regname) unwoundState->m_Capture.regname = ctx.regname;
    ENUM_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER

    // Since we don't have context pointers in this case, just assing them to NULL.
#define CALLEE_SAVED_REGISTER(regname) unwoundState->m_Ptrs.p##regname = NULL;
    ENUM_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER

#else  // !DACCESS_COMPILE

#define CALLEE_SAVED_REGISTER(regname) unwoundState->m_Ptrs.p##regname = PTR_ULONG64(nonVolRegPtrs.regname);
    ENUM_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER

#endif // DACCESS_COMPILE 
}