1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/profiler/win32_stack_frame_unwinder.h"
6
7 #include <windows.h>
8
9 #include <utility>
10
11 #include "base/check_op.h"
12 #include "base/notreached.h"
13 #include "build/build_config.h"
14
15 namespace base {
16
17 // Win32UnwindFunctions -------------------------------------------------------
18
19 namespace {
20
21 // Implements the UnwindFunctions interface for the corresponding Win32
22 // functions.
23 class Win32UnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions {
24 public:
25 Win32UnwindFunctions();
26
27 Win32UnwindFunctions(const Win32UnwindFunctions&) = delete;
28 Win32UnwindFunctions& operator=(const Win32UnwindFunctions&) = delete;
29
30 ~Win32UnwindFunctions() override;
31
32 PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
33 PDWORD64 image_base) override;
34
35 void VirtualUnwind(DWORD64 image_base,
36 DWORD64 program_counter,
37 PRUNTIME_FUNCTION runtime_function,
38 CONTEXT* context) override;
39 };
40
41 Win32UnwindFunctions::Win32UnwindFunctions() = default;
42 Win32UnwindFunctions::~Win32UnwindFunctions() = default;
43
LookupFunctionEntry(DWORD64 program_counter,PDWORD64 image_base)44 PRUNTIME_FUNCTION Win32UnwindFunctions::LookupFunctionEntry(
45 DWORD64 program_counter,
46 PDWORD64 image_base) {
47 #if defined(ARCH_CPU_64_BITS)
48 return ::RtlLookupFunctionEntry(program_counter, image_base, nullptr);
49 #else
50 NOTREACHED();
51 #endif
52 }
53
VirtualUnwind(DWORD64 image_base,DWORD64 program_counter,PRUNTIME_FUNCTION runtime_function,CONTEXT * context)54 void Win32UnwindFunctions::VirtualUnwind(DWORD64 image_base,
55 DWORD64 program_counter,
56 PRUNTIME_FUNCTION runtime_function,
57 CONTEXT* context) {
58 #if defined(ARCH_CPU_64_BITS)
59 void* handler_data = nullptr;
60 ULONG64 establisher_frame;
61 KNONVOLATILE_CONTEXT_POINTERS nvcontext = {};
62 ::RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, program_counter,
63 runtime_function, context, &handler_data,
64 &establisher_frame, &nvcontext);
65 #else
66 NOTREACHED();
67 #endif
68 }
69
70 } // namespace
71
72 // Win32StackFrameUnwinder ----------------------------------------------------
73
74 Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() = default;
75 Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() = default;
76
Win32StackFrameUnwinder()77 Win32StackFrameUnwinder::Win32StackFrameUnwinder()
78 : Win32StackFrameUnwinder(std::make_unique<Win32UnwindFunctions>()) {}
79
80 Win32StackFrameUnwinder::~Win32StackFrameUnwinder() = default;
81
TryUnwind(bool at_top_frame,CONTEXT * context,const ModuleCache::Module * module)82 bool Win32StackFrameUnwinder::TryUnwind(
83 bool at_top_frame,
84 CONTEXT* context,
85 // The module parameter, while not directly used, is still passed because it
86 // represents an implicit dependency for this function. Having the Module
87 // ensures that we have incremented the HMODULE reference count, which is
88 // critical to ensuring that the module is not unloaded during the
89 // unwinding. Otherwise the module could be unloaded between the
90 // LookupFunctionEntry and VirtualUnwind calls, resulting in crashes
91 // accessing unwind information from the unloaded module.
92 const ModuleCache::Module* module) {
93 #if defined(ARCH_CPU_64_BITS)
94 // Ensure we found a valid module for the program counter.
95 DCHECK(module);
96 ULONG64 image_base = 0;
97 // Try to look up unwind metadata for the current function.
98 PRUNTIME_FUNCTION runtime_function =
99 unwind_functions_->LookupFunctionEntry(ContextPC(context), &image_base);
100
101 if (runtime_function) {
102 DCHECK_EQ(module->GetBaseAddress(), image_base);
103 unwind_functions_->VirtualUnwind(image_base, ContextPC(context),
104 runtime_function, context);
105 return true;
106 }
107
108 if (at_top_frame) {
109 // This is a leaf function (i.e. a function that neither calls a function,
110 // nor allocates any stack space itself).
111 #if defined(ARCH_CPU_X86_64)
112 // For X64, return address is at RSP.
113 context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp);
114 context->Rsp += 8;
115 #elif defined(ARCH_CPU_ARM64)
116 // For leaf function on Windows ARM64, return address is at LR(X30). Add
117 // CONTEXT_UNWOUND_TO_CALL flag to avoid unwind ambiguity for tailcall on
118 // ARM64, because padding after tailcall is not guaranteed.
119 context->Pc = context->Lr;
120 context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
121 #else
122 #error Unsupported Windows 64-bit Arch
123 #endif
124 return true;
125 }
126
127 // In theory we shouldn't get here, as it means we've encountered a function
128 // without unwind information below the top of the stack, which is forbidden
129 // by the Microsoft x64 calling convention.
130 //
131 // The one known case in Chrome code that executes this path occurs because
132 // of BoringSSL unwind information inconsistent with the actual function
133 // code. See https://crbug.com/542919.
134 return false;
135 #else
136 NOTREACHED();
137 #endif
138 }
139
Win32StackFrameUnwinder(std::unique_ptr<UnwindFunctions> unwind_functions)140 Win32StackFrameUnwinder::Win32StackFrameUnwinder(
141 std::unique_ptr<UnwindFunctions> unwind_functions)
142 : unwind_functions_(std::move(unwind_functions)) {}
143
144 } // namespace base
145