summaryrefslogtreecommitdiff
path: root/deps/v8/src/unwinder.cc
blob: b0b6ee05048a756928a8e32e1598777d31de6497 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "include/v8.h"
#include "src/frame-constants.h"
#include "src/globals.h"

namespace v8 {

namespace {

bool PCIsInCodeRange(const v8::MemoryRange& code_range, void* pc) {
  // Given that the length of the memory range is in bytes and it is not
  // necessarily aligned, we need to do the pointer arithmetic in byte* here.
  const i::byte* pc_as_byte = reinterpret_cast<i::byte*>(pc);
  const i::byte* start = reinterpret_cast<const i::byte*>(code_range.start);
  const i::byte* end = start + code_range.length_in_bytes;
  return pc_as_byte >= start && pc_as_byte < end;
}

bool IsInUnsafeJSEntryRange(const v8::JSEntryStub& js_entry_stub, void* pc) {
  return PCIsInCodeRange(js_entry_stub.code, pc);

  // TODO(petermarshall): We can be more precise by checking whether we are
  // in JSEntry but after frame setup and before frame teardown, in which case
  // we are safe to unwind the stack. For now, we bail out if the PC is anywhere
  // within JSEntry.
}

i::Address Load(i::Address address) {
  return *reinterpret_cast<i::Address*>(address);
}

void* GetReturnAddressFromFP(void* fp) {
  return reinterpret_cast<void*>(
      Load(reinterpret_cast<i::Address>(fp) +
           i::CommonFrameConstants::kCallerPCOffset));
}

void* GetCallerFPFromFP(void* fp) {
  return reinterpret_cast<void*>(
      Load(reinterpret_cast<i::Address>(fp) +
           i::CommonFrameConstants::kCallerFPOffset));
}

void* GetCallerSPFromFP(void* fp) {
  return reinterpret_cast<void*>(reinterpret_cast<i::Address>(fp) +
                                 i::CommonFrameConstants::kCallerSPOffset);
}

bool AddressIsInStack(const void* address, const void* stack_base,
                      const void* stack_top) {
  return address <= stack_base && address >= stack_top;
}

}  // namespace

bool Unwinder::TryUnwindV8Frames(const UnwindState& unwind_state,
                                 RegisterState* register_state,
                                 const void* stack_base) {
  const void* stack_top = register_state->sp;

  void* pc = register_state->pc;
  if (PCIsInV8(unwind_state, pc) &&
      !IsInUnsafeJSEntryRange(unwind_state.js_entry_stub, pc)) {
    void* current_fp = register_state->fp;
    if (!AddressIsInStack(current_fp, stack_base, stack_top)) return false;

    // Peek at the return address that the caller pushed. If it's in V8, then we
    // assume the caller frame is a JS frame and continue to unwind.
    void* next_pc = GetReturnAddressFromFP(current_fp);
    while (PCIsInV8(unwind_state, next_pc)) {
      current_fp = GetCallerFPFromFP(current_fp);
      if (!AddressIsInStack(current_fp, stack_base, stack_top)) return false;
      next_pc = GetReturnAddressFromFP(current_fp);
    }

    void* final_sp = GetCallerSPFromFP(current_fp);
    if (!AddressIsInStack(final_sp, stack_base, stack_top)) return false;
    register_state->sp = final_sp;

    void* final_fp = GetCallerFPFromFP(current_fp);
    if (!AddressIsInStack(final_fp, stack_base, stack_top)) return false;
    register_state->fp = final_fp;

    register_state->pc = next_pc;
    return true;
  }
  return false;
}

bool Unwinder::PCIsInV8(const UnwindState& unwind_state, void* pc) {
  return pc && (PCIsInCodeRange(unwind_state.code_range, pc) ||
                PCIsInCodeRange(unwind_state.embedded_code_range, pc));
}

}  // namespace v8