-
Notifications
You must be signed in to change notification settings - Fork 121
/
win32_stack_frame_unwinder.cc
145 lines (119 loc) · 4.97 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
// Copyright 2015 The Chromium Authors
// 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/notreached.h"
#include "build/build_config.h"
namespace base {
// Win32UnwindFunctions -------------------------------------------------------
namespace {
// Implements the UnwindFunctions interface for the corresponding Win32
// functions.
class Win32UnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions {
public:
Win32UnwindFunctions();
Win32UnwindFunctions(const Win32UnwindFunctions&) = delete;
Win32UnwindFunctions& operator=(const Win32UnwindFunctions&) = delete;
~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;
};
Win32UnwindFunctions::Win32UnwindFunctions() {}
Win32UnwindFunctions::~Win32UnwindFunctions() {}
PRUNTIME_FUNCTION Win32UnwindFunctions::LookupFunctionEntry(
DWORD64 program_counter,
PDWORD64 image_base) {
#if defined(ARCH_CPU_64_BITS)
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) {
#if defined(ARCH_CPU_64_BITS)
void* handler_data = nullptr;
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
}
} // namespace
// Win32StackFrameUnwinder ----------------------------------------------------
Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() = default;
Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() = default;
Win32StackFrameUnwinder::Win32StackFrameUnwinder()
: Win32StackFrameUnwinder(std::make_unique<Win32UnwindFunctions>()) {}
Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {}
bool Win32StackFrameUnwinder::TryUnwind(
bool at_top_frame,
CONTEXT* context,
// The module parameter, while not directly used, is still passed because it
// represents an implicit dependency for this function. Having the Module
// ensures that we have incremented the HMODULE reference count, which is
// critical to ensuring that the module is not unloaded during the
// unwinding. Otherwise the module could be unloaded between the
// LookupFunctionEntry and VirtualUnwind calls, resulting in crashes
// accessing unwind information from the unloaded module.
const ModuleCache::Module* module) {
#if defined(ARCH_CPU_64_BITS)
// Ensure we found a valid module for the program counter.
DCHECK(module);
ULONG64 image_base = 0;
// Try to look up unwind metadata for the current function.
PRUNTIME_FUNCTION runtime_function =
unwind_functions_->LookupFunctionEntry(ContextPC(context), &image_base);
if (runtime_function) {
DCHECK_EQ(module->GetBaseAddress(), image_base);
unwind_functions_->VirtualUnwind(image_base, ContextPC(context),
runtime_function, context);
return true;
}
if (at_top_frame) {
// This is a leaf function (i.e. a function that neither calls a function,
// nor allocates any stack space itself).
#if defined(ARCH_CPU_X86_64)
// For X64, return address is at RSP.
context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp);
context->Rsp += 8;
#elif defined(ARCH_CPU_ARM64)
// For leaf function on Windows ARM64, return address is at LR(X30). Add
// CONTEXT_UNWOUND_TO_CALL flag to avoid unwind ambiguity for tailcall on
// ARM64, because padding after tailcall is not guaranteed.
context->Pc = context->Lr;
context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
#else
#error Unsupported Windows 64-bit Arch
#endif
return true;
}
// 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.
return false;
#else
NOTREACHED();
return false;
#endif
}
Win32StackFrameUnwinder::Win32StackFrameUnwinder(
std::unique_ptr<UnwindFunctions> unwind_functions)
: unwind_functions_(std::move(unwind_functions)) {}
} // namespace base