• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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