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
9namespace v8 {
10
11namespace {
12
13bool 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 const i::byte* start = reinterpret_cast<const i::byte*>(code_range.start);
18 const i::byte* end = start + code_range.length_in_bytes;
19 return pc_as_byte >= start && pc_as_byte < end;
20}
21
22bool 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
31i::Address Load(i::Address address) {
32 return *reinterpret_cast<i::Address*>(address);
33}
34
35void* GetReturnAddressFromFP(void* fp) {
36 return reinterpret_cast<void*>(
37 Load(reinterpret_cast<i::Address>(fp) +
38 i::CommonFrameConstants::kCallerPCOffset));
39}
40
41void* GetCallerFPFromFP(void* fp) {
42 return reinterpret_cast<void*>(
43 Load(reinterpret_cast<i::Address>(fp) +
44 i::CommonFrameConstants::kCallerFPOffset));
45}
46
47void* GetCallerSPFromFP(void* fp) {
48 return reinterpret_cast<void*>(reinterpret_cast<i::Address>(fp) +
49 i::CommonFrameConstants::kCallerSPOffset);
50}
51
52bool AddressIsInStack(const void* address, const void* stack_base,
53 const void* stack_top) {
54 return address <= stack_base && address >= stack_top;
55}
56
57} // namespace
58
59bool Unwinder::TryUnwindV8Frames(const UnwindState& unwind_state,
60 RegisterState* register_state,
61 const void* stack_base) {
62 const void* stack_top = register_state->sp;
63
64 void* pc = register_state->pc;
65 if (PCIsInV8(unwind_state, pc) &&
66 !IsInUnsafeJSEntryRange(unwind_state.js_entry_stub, pc)) {
67 void* current_fp = register_state->fp;
68 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 while (PCIsInV8(unwind_state, next_pc)) {
74 current_fp = GetCallerFPFromFP(current_fp);
75 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 if (!AddressIsInStack(final_sp, stack_base, stack_top)) return false;
81 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 register_state->fp = final_fp;
89
90 register_state->pc = next_pc;
91 return true;
92 }
93 return false;
94}
95
96bool Unwinder::PCIsInV8(const UnwindState& unwind_state, void* pc) {
97 return pc && (PCIsInCodeRange(unwind_state.code_range, pc) ||
98 PCIsInCodeRange(unwind_state.embedded_code_range, pc));
99}
100
101} // namespace v8
102