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
Win32UnwindFunctions()41 Win32UnwindFunctions::Win32UnwindFunctions() {}
~Win32UnwindFunctions()42 Win32UnwindFunctions::~Win32UnwindFunctions() {}
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 return nullptr;
52 #endif
53 }
54
VirtualUnwind(DWORD64 image_base,DWORD64 program_counter,PRUNTIME_FUNCTION runtime_function,CONTEXT * context)55 void Win32UnwindFunctions::VirtualUnwind(DWORD64 image_base,
56 DWORD64 program_counter,
57 PRUNTIME_FUNCTION runtime_function,
58 CONTEXT* context) {
59 #if defined(ARCH_CPU_64_BITS)
60 void* handler_data = nullptr;
61 ULONG64 establisher_frame;
62 KNONVOLATILE_CONTEXT_POINTERS nvcontext = {};
63 ::RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, program_counter,
64 runtime_function, context, &handler_data,
65 &establisher_frame, &nvcontext);
66 #else
67 NOTREACHED();
68 #endif
69 }
70
71 } // namespace
72
73 // Win32StackFrameUnwinder ----------------------------------------------------
74
75 Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() = default;
76 Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() = default;
77
Win32StackFrameUnwinder()78 Win32StackFrameUnwinder::Win32StackFrameUnwinder()
79 : Win32StackFrameUnwinder(std::make_unique<Win32UnwindFunctions>()) {}
80
~Win32StackFrameUnwinder()81 Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {}
82
TryUnwind(bool at_top_frame,CONTEXT * context,const ModuleCache::Module * module)83 bool Win32StackFrameUnwinder::TryUnwind(
84 bool at_top_frame,
85 CONTEXT* context,
86 // The module parameter, while not directly used, is still passed because it
87 // represents an implicit dependency for this function. Having the Module
88 // ensures that we have incremented the HMODULE reference count, which is
89 // critical to ensuring that the module is not unloaded during the
90 // unwinding. Otherwise the module could be unloaded between the
91 // LookupFunctionEntry and VirtualUnwind calls, resulting in crashes
92 // accessing unwind information from the unloaded module.
93 const ModuleCache::Module* module) {
94 #if defined(ARCH_CPU_64_BITS)
95 // Ensure we found a valid module for the program counter.
96 DCHECK(module);
97 ULONG64 image_base = 0;
98 // Try to look up unwind metadata for the current function.
99 PRUNTIME_FUNCTION runtime_function =
100 unwind_functions_->LookupFunctionEntry(ContextPC(context), &image_base);
101
102 if (runtime_function) {
103 DCHECK_EQ(module->GetBaseAddress(), image_base);
104 unwind_functions_->VirtualUnwind(image_base, ContextPC(context),
105 runtime_function, context);
106 return true;
107 }
108
109 if (at_top_frame) {
110 // This is a leaf function (i.e. a function that neither calls a function,
111 // nor allocates any stack space itself).
112 #if defined(ARCH_CPU_X86_64)
113 // For X64, return address is at RSP.
114 context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp);
115 context->Rsp += 8;
116 #elif defined(ARCH_CPU_ARM64)
117 // For leaf function on Windows ARM64, return address is at LR(X30). Add
118 // CONTEXT_UNWOUND_TO_CALL flag to avoid unwind ambiguity for tailcall on
119 // ARM64, because padding after tailcall is not guaranteed.
120 context->Pc = context->Lr;
121 context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
122 #else
123 #error Unsupported Windows 64-bit Arch
124 #endif
125 return true;
126 }
127
128 // In theory we shouldn't get here, as it means we've encountered a function
129 // without unwind information below the top of the stack, which is forbidden
130 // by the Microsoft x64 calling convention.
131 //
132 // The one known case in Chrome code that executes this path occurs because
133 // of BoringSSL unwind information inconsistent with the actual function
134 // code. See https://crbug.com/542919.
135 return false;
136 #else
137 NOTREACHED();
138 return false;
139 #endif
140 }
141
Win32StackFrameUnwinder(std::unique_ptr<UnwindFunctions> unwind_functions)142 Win32StackFrameUnwinder::Win32StackFrameUnwinder(
143 std::unique_ptr<UnwindFunctions> unwind_functions)
144 : unwind_functions_(std::move(unwind_functions)) {}
145
146 } // namespace base
147