Line data Source code
1 : // Copyright 2018 the V8 project authors. All rights reserved.
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 "include/v8.h"
6 : #include "src/frame-constants.h"
7 : #include "src/globals.h"
8 :
9 : namespace v8 {
10 :
11 : namespace {
12 :
13 : bool PCIsInCodeRange(const v8::MemoryRange& code_range, void* pc) {
14 : // Given that the length of the memory range is in bytes and it is not
15 : // necessarily aligned, we need to do the pointer arithmetic in byte* here.
16 : const i::byte* pc_as_byte = reinterpret_cast<i::byte*>(pc);
17 361 : const i::byte* start = reinterpret_cast<const i::byte*>(code_range.start);
18 361 : const i::byte* end = start + code_range.length_in_bytes;
19 361 : return pc_as_byte >= start && pc_as_byte < end;
20 : }
21 :
22 : bool IsInUnsafeJSEntryRange(const v8::JSEntryStub& js_entry_stub, void* pc) {
23 : return PCIsInCodeRange(js_entry_stub.code, pc);
24 :
25 : // TODO(petermarshall): We can be more precise by checking whether we are
26 : // in JSEntry but after frame setup and before frame teardown, in which case
27 : // we are safe to unwind the stack. For now, we bail out if the PC is anywhere
28 : // within JSEntry.
29 : }
30 :
31 : i::Address Load(i::Address address) {
32 108 : return *reinterpret_cast<i::Address*>(address);
33 : }
34 :
35 : void* GetReturnAddressFromFP(void* fp) {
36 : return reinterpret_cast<void*>(
37 54 : Load(reinterpret_cast<i::Address>(fp) +
38 54 : i::CommonFrameConstants::kCallerPCOffset));
39 : }
40 :
41 : void* GetCallerFPFromFP(void* fp) {
42 : return reinterpret_cast<void*>(
43 : Load(reinterpret_cast<i::Address>(fp) +
44 54 : i::CommonFrameConstants::kCallerFPOffset));
45 : }
46 :
47 : void* GetCallerSPFromFP(void* fp) {
48 34 : return reinterpret_cast<void*>(reinterpret_cast<i::Address>(fp) +
49 34 : i::CommonFrameConstants::kCallerSPOffset);
50 : }
51 :
52 : bool AddressIsInStack(const void* address, const void* stack_base,
53 : const void* stack_top) {
54 98 : return address <= stack_base && address >= stack_top;
55 : }
56 :
57 : } // namespace
58 :
59 64 : bool Unwinder::TryUnwindV8Frames(const UnwindState& unwind_state,
60 : RegisterState* register_state,
61 : const void* stack_base) {
62 64 : const void* stack_top = register_state->sp;
63 :
64 64 : void* pc = register_state->pc;
65 123 : if (PCIsInV8(unwind_state, pc) &&
66 : !IsInUnsafeJSEntryRange(unwind_state.js_entry_stub, pc)) {
67 44 : void* current_fp = register_state->fp;
68 44 : if (!AddressIsInStack(current_fp, stack_base, stack_top)) return false;
69 :
70 : // Peek at the return address that the caller pushed. If it's in V8, then we
71 : // assume the caller frame is a JS frame and continue to unwind.
72 : void* next_pc = GetReturnAddressFromFP(current_fp);
73 69 : while (PCIsInV8(unwind_state, next_pc)) {
74 : current_fp = GetCallerFPFromFP(current_fp);
75 20 : if (!AddressIsInStack(current_fp, stack_base, stack_top)) return false;
76 : next_pc = GetReturnAddressFromFP(current_fp);
77 : }
78 :
79 : void* final_sp = GetCallerSPFromFP(current_fp);
80 34 : if (!AddressIsInStack(final_sp, stack_base, stack_top)) return false;
81 34 : register_state->sp = final_sp;
82 :
83 : // We don't check that the final FP value is within the stack bounds because
84 : // this is just the rbp value that JSEntryStub pushed. On platforms like
85 : // Win64 this is not used as a dedicated FP register, and could contain
86 : // anything.
87 : void* final_fp = GetCallerFPFromFP(current_fp);
88 34 : register_state->fp = final_fp;
89 :
90 34 : register_state->pc = next_pc;
91 34 : return true;
92 : }
93 : return false;
94 : }
95 :
96 90 : bool Unwinder::PCIsInV8(const UnwindState& unwind_state, void* pc) {
97 510 : return pc && (PCIsInCodeRange(unwind_state.code_range, pc) ||
98 90 : PCIsInCodeRange(unwind_state.embedded_code_range, pc));
99 : }
100 :
101 : } // namespace v8
|