summaryrefslogtreecommitdiff
path: root/src/debug/daccess/unwinder.cpp
blob: 6431a83ea86c154e62109446f8e36c5521dbffa6 (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
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

// 

#include "stdafx.h"
#include "unwinder.h"

EXTERN_C void GetRuntimeStackWalkInfo(IN  ULONG64   ControlPc,
                                      OUT UINT_PTR* pModuleBase,
                                      OUT UINT_PTR* pFuncEntry);

#if !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_)
//---------------------------------------------------------------------------------------
//
// Read an UNWIND_INFO structure given its address.  The UNWIND_INFO structure is variable sized.
// This is just a simple wrapper over the platform-specific DAC function which does the real work.
//
// Arguments:
//    taUnwindInfo - target address of the beginning of the UNWIND_INFO structure
//
// Return Value:
//   Return the specified UNWIND_INFO.  It lives in the DAC cache, so the caller doesn't need to explicitly
//   free the memory.  It'll get flushed when the DAC cache is flushed (i.e. when we continue).
//

UNWIND_INFO * OOPStackUnwinder::GetUnwindInfo(TADDR taUnwindInfo)
{
    return DacGetUnwindInfo(taUnwindInfo);
}
#endif // !_TARGET_ARM_ && !_TARGET_ARM64_

//---------------------------------------------------------------------------------------
//
// This is a simple wrapper over code:OOPStackUnwinder::ReadMemory().  Unlike ReadMemory(), 
// it fails if we don't successfully read all the specified bytes.
//
// Arguments:
//    address   - the address to be read
//    pbBuffer  - the buffer to store the read memory
//    cbRequest - the number of bytes requested
//
// Return Value:
//   S_OK if all the memory is read successfully.
//   HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY) if only part of the memory is read.
//   Failure HRs otherwise.
//

HRESULT OOPStackUnwinder::ReadAllMemory(                       DWORD64 address,
                                        __in_ecount(cbRequest) PVOID   pbBuffer,
                                                               DWORD   cbRequest)
{
    DWORD cbDone = 0;
    HRESULT hr = ReadMemory(address, pbBuffer, cbRequest, &cbDone);
    if (SUCCEEDED(hr) && (cbDone != cbRequest))
    {
        return HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY);
    }
    else
    {
        return hr;
    }
}

//---------------------------------------------------------------------------------------
//
// Read the specified memory from out-of-process.  This function uses DacReadAll() to do the trick.
//
// Arguments:
//    address   - the address to be read
//    pbBuffer  - the buffer to store the read memory
//    cbRequest - the number of bytes requested
//    pcbDone   - out parameter; returns the number of bytes read
//
// Return Value:
//   S_OK if all the memory is read successfully
//

HRESULT OOPStackUnwinder::ReadMemory(                       DWORD64 address,
                                       __in_ecount(cbRequest) PVOID   pbBuffer,
                                                              DWORD   cbRequest,
                                       __out_opt              PDWORD  pcbDone)
{
    _ASSERTE(pcbDone != NULL);

    HRESULT hr = DacReadAll(TO_TADDR(address), pbBuffer, cbRequest, false);
    if (SUCCEEDED(hr))
    {
        *pcbDone = cbRequest;

        // On X64, we need to replace any patches which are within the requested memory range.
        // This is because the X64 unwinder needs to disassemble the native instructions in order to determine
        // whether the IP is in an epilog.
#if defined(_TARGET_AMD64_)
        MemoryRange range(dac_cast<PTR_VOID>((TADDR)address), cbRequest);
        hr = DacReplacePatchesInHostMemory(range, pbBuffer);
#endif // _TARGET_AMD64_
    }
    else
    {
        *pcbDone = 0;
    }

    return hr;
}

//---------------------------------------------------------------------------------------
//
// Given a control PC, return the base of the module it is in.  For jitted managed code, this is the 
// start of the code heap.
//
// Arguments:
//    address - the specified address
//    pdwBase - out parameter; returns the module base
//
// Return Value:
//    S_OK if we retrieve the module base successfully;
//    E_FAIL otherwise
//

HRESULT OOPStackUnwinder::GetModuleBase(      DWORD64  address,
                                          __out PDWORD64 pdwBase)
{
    GetRuntimeStackWalkInfo(address, reinterpret_cast<UINT_PTR *>(pdwBase), NULL);
    return ((*pdwBase == NULL) ? E_FAIL : S_OK);
}

//---------------------------------------------------------------------------------------
//
// Given a control PC, return the function entry of the functoin it is in.
//
// Arguments:
//    address  - the specified IP
//    pBuffer  - the buffer to store the retrieved function entry
//    cbBuffer - the size of the buffer
//
// Return Value:
//    S_OK          if we retrieve the function entry successfully;
//    E_INVALIDARG  if the buffer is too small;
//    E_FAIL        otherwise
//

HRESULT OOPStackUnwinder::GetFunctionEntry(                       DWORD64 address,
                                           __out_ecount(cbBuffer) PVOID   pBuffer,
                                                                  DWORD   cbBuffer)
{
    if (cbBuffer < sizeof(RUNTIME_FUNCTION))
    {
        return E_INVALIDARG;
    }

    PVOID pFuncEntry = NULL;
    GetRuntimeStackWalkInfo(address, NULL, reinterpret_cast<UINT_PTR *>(&pFuncEntry));
    if (pFuncEntry == NULL)
    {
        return E_FAIL;
    }

    memcpy(pBuffer, pFuncEntry, cbBuffer);
    return S_OK;
}