This repository has been archived by the owner on Jan 18, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwin32_stack_frame_unwinder.cc
186 lines (153 loc) · 6.26 KB
/
win32_stack_frame_unwinder.cc
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
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/profiler/win32_stack_frame_unwinder.h"
#include <windows.h>
#include <utility>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
namespace base {
// Win32UnwindFunctions -------------------------------------------------------
const HMODULE ModuleHandleTraits::kNonNullModuleForTesting =
reinterpret_cast<HMODULE>(static_cast<uintptr_t>(-1));
// static
bool ModuleHandleTraits::CloseHandle(HMODULE handle) {
if (handle == kNonNullModuleForTesting)
return true;
return ::FreeLibrary(handle) != 0;
}
// static
bool ModuleHandleTraits::IsHandleValid(HMODULE handle) {
return handle != nullptr;
}
// static
HMODULE ModuleHandleTraits::NullHandle() {
return nullptr;
}
namespace {
// Implements the UnwindFunctions interface for the corresponding Win32
// functions.
class Win32UnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions {
public:
Win32UnwindFunctions();
~Win32UnwindFunctions() override;
PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
PDWORD64 image_base) override;
void VirtualUnwind(DWORD64 image_base,
DWORD64 program_counter,
PRUNTIME_FUNCTION runtime_function,
CONTEXT* context) override;
ScopedModuleHandle GetModuleForProgramCounter(
DWORD64 program_counter) override;
private:
DISALLOW_COPY_AND_ASSIGN(Win32UnwindFunctions);
};
Win32UnwindFunctions::Win32UnwindFunctions() {}
Win32UnwindFunctions::~Win32UnwindFunctions() {}
PRUNTIME_FUNCTION Win32UnwindFunctions::LookupFunctionEntry(
DWORD64 program_counter,
PDWORD64 image_base) {
#ifdef _WIN64
return RtlLookupFunctionEntry(program_counter, image_base, nullptr);
#else
NOTREACHED();
return nullptr;
#endif
}
void Win32UnwindFunctions::VirtualUnwind(DWORD64 image_base,
DWORD64 program_counter,
PRUNTIME_FUNCTION runtime_function,
CONTEXT* context) {
#ifdef _WIN64
void* handler_data;
ULONG64 establisher_frame;
KNONVOLATILE_CONTEXT_POINTERS nvcontext = {};
RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, program_counter,
runtime_function, context, &handler_data,
&establisher_frame, &nvcontext);
#else
NOTREACHED();
#endif
}
ScopedModuleHandle Win32UnwindFunctions::GetModuleForProgramCounter(
DWORD64 program_counter) {
HMODULE module_handle = nullptr;
// GetModuleHandleEx() increments the module reference count, which is then
// managed and ultimately decremented by ScopedModuleHandle.
if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
reinterpret_cast<LPCTSTR>(program_counter),
&module_handle)) {
const DWORD error = ::GetLastError();
DCHECK_EQ(ERROR_MOD_NOT_FOUND, static_cast<int>(error));
}
return ScopedModuleHandle(module_handle);
}
} // namespace
// Win32StackFrameUnwinder ----------------------------------------------------
Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {}
Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {}
Win32StackFrameUnwinder::Win32StackFrameUnwinder()
: Win32StackFrameUnwinder(WrapUnique(new Win32UnwindFunctions)) {}
Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {}
bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context,
ScopedModuleHandle* module) {
#ifdef _WIN64
ScopedModuleHandle frame_module =
unwind_functions_->GetModuleForProgramCounter(context->Rip);
if (!frame_module.IsValid()) {
// There's no loaded module containing the instruction pointer. This can be
// due to executing code that is not in a module. In particular,
// runtime-generated code associated with third-party injected DLLs
// typically is not in a module. It can also be due to the the module having
// been unloaded since we recorded the stack. In the latter case the
// function unwind information was part of the unloaded module, so it's not
// possible to unwind further.
//
// If a module was found, it's still theoretically possible for the detected
// module module to be different than the one that was loaded when the stack
// was copied (i.e. if the module was unloaded and a different module loaded
// in overlapping memory). This likely would cause a crash, but has not been
// observed in practice.
return false;
}
ULONG64 image_base;
// Try to look up unwind metadata for the current function.
PRUNTIME_FUNCTION runtime_function =
unwind_functions_->LookupFunctionEntry(context->Rip, &image_base);
if (runtime_function) {
unwind_functions_->VirtualUnwind(image_base, context->Rip, runtime_function,
context);
at_top_frame_ = false;
} else {
if (at_top_frame_) {
at_top_frame_ = false;
// This is a leaf function (i.e. a function that neither calls a function,
// nor allocates any stack space itself) so the return address is at RSP.
context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp);
context->Rsp += 8;
} else {
// In theory we shouldn't get here, as it means we've encountered a
// function without unwind information below the top of the stack, which
// is forbidden by the Microsoft x64 calling convention.
//
// The one known case in Chrome code that executes this path occurs
// because of BoringSSL unwind information inconsistent with the actual
// function code. See https://crbug.com/542919.
//
// Note that dodgy third-party generated code that otherwise would enter
// this path should be caught by the module check above, since the code
// typically is located outside of a module.
return false;
}
}
module->Set(frame_module.Take());
return true;
#else
NOTREACHED();
return false;
#endif
}
Win32StackFrameUnwinder::Win32StackFrameUnwinder(
std::unique_ptr<UnwindFunctions> unwind_functions)
: at_top_frame_(true), unwind_functions_(std::move(unwind_functions)) {}
} // namespace base