1 // Copyright 2019 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/native_unwinder_win.h"
6
7 #include <winnt.h>
8
9 #include "base/check_op.h"
10 #include "base/notreached.h"
11 #include "base/profiler/win32_stack_frame_unwinder.h"
12 #include "build/build_config.h"
13
14 namespace base {
15
CanUnwindFrom(const Frame & current_frame) const16 bool NativeUnwinderWin::CanUnwindFrom(const Frame& current_frame) const {
17 return current_frame.module && current_frame.module->IsNative();
18 }
19
20 // Attempts to unwind the frame represented by the context values. If
21 // successful appends frames onto the stack and returns true. Otherwise
22 // returns false.
TryUnwind(RegisterContext * thread_context,uintptr_t stack_top,std::vector<Frame> * stack)23 UnwindResult NativeUnwinderWin::TryUnwind(RegisterContext* thread_context,
24 uintptr_t stack_top,
25 std::vector<Frame>* stack) {
26 // We expect the frame corresponding to the |thread_context| register state to
27 // exist within |stack|.
28 DCHECK_GT(stack->size(), 0u);
29
30 Win32StackFrameUnwinder frame_unwinder;
31 for (;;) {
32 if (!stack->back().module) {
33 // There's no loaded module corresponding to the current frame. This can
34 // be due to executing code not in a module (e.g. runtime-generated code
35 // associated with third-party injected DLLs) or the module having been
36 // unloaded since we recorded the stack. In the latter case the function
37 // unwind information was part of the unloaded module, so it's not
38 // possible to unwind further.
39 //
40 // NB: if a module was found it's still theoretically possible for the
41 // detected module module to be different than the one that was loaded
42 // when the stack was copied, if the module was unloaded and a different
43 // module loaded in overlapping memory. This likely would cause a crash
44 // but has not been observed in practice.
45 return UnwindResult::kAborted;
46 }
47
48 if (!stack->back().module->IsNative()) {
49 // This is a non-native module associated with the auxiliary unwinder
50 // (e.g. corresponding to a frame in V8 generated code). Report as
51 // UNRECOGNIZED_FRAME to allow that unwinder to unwind the frame.
52 return UnwindResult::kUnrecognizedFrame;
53 }
54
55 uintptr_t prev_stack_pointer = RegisterContextStackPointer(thread_context);
56 if (!frame_unwinder.TryUnwind(stack->size() == 1u, thread_context,
57 stack->back().module)) {
58 return UnwindResult::kAborted;
59 }
60
61 if (RegisterContextInstructionPointer(thread_context) == 0)
62 return UnwindResult::kCompleted;
63
64 // Exclusive range of expected stack pointer values after the unwind.
65 struct {
66 uintptr_t start;
67 uintptr_t end;
68 } expected_stack_pointer_range = {prev_stack_pointer, stack_top};
69
70 // Abort if the unwind produced an invalid stack pointer.
71 #if defined(ARCH_CPU_ARM64)
72 // Leaf frames on Arm can re-use the stack pointer, so they can validly have
73 // the same stack pointer as the previous frame.
74 if (stack->size() == 1u) {
75 expected_stack_pointer_range.start--;
76 }
77 #endif
78 if (RegisterContextStackPointer(thread_context) <=
79 expected_stack_pointer_range.start ||
80 RegisterContextStackPointer(thread_context) >=
81 expected_stack_pointer_range.end) {
82 return UnwindResult::kAborted;
83 }
84
85 // Record the frame to which we just unwound.
86 stack->emplace_back(RegisterContextInstructionPointer(thread_context),
87 module_cache()->GetModuleForAddress(
88 RegisterContextInstructionPointer(thread_context)));
89 }
90
91 NOTREACHED();
92 return UnwindResult::kCompleted;
93 }
94
95 } // namespace base
96