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