xref: /aosp_15_r20/external/cronet/base/profiler/win32_stack_frame_unwinder.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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