11cb0ef41Sopenharmony_ci// Copyright 2018 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/diagnostics/unwinder.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include <algorithm>
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ci#include "include/v8-unwinder.h"
101cb0ef41Sopenharmony_ci#include "src/execution/frame-constants.h"
111cb0ef41Sopenharmony_ci#include "src/execution/pointer-authentication.h"
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_cinamespace v8 {
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ci// Architecture specific. Implemented in unwinder-<arch>.cc.
161cb0ef41Sopenharmony_civoid GetCalleeSavedRegistersFromEntryFrame(void* fp,
171cb0ef41Sopenharmony_ci                                           RegisterState* register_state);
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_cii::Address Load(i::Address address) {
201cb0ef41Sopenharmony_ci  return *reinterpret_cast<i::Address*>(address);
211cb0ef41Sopenharmony_ci}
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_cinamespace {
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ciconst i::byte* CalculateEnd(const void* start, size_t length_in_bytes) {
261cb0ef41Sopenharmony_ci  // Given that the length of the memory range is in bytes and it is not
271cb0ef41Sopenharmony_ci  // necessarily aligned, we need to do the pointer arithmetic in byte* here.
281cb0ef41Sopenharmony_ci  const i::byte* start_as_byte = reinterpret_cast<const i::byte*>(start);
291cb0ef41Sopenharmony_ci  return start_as_byte + length_in_bytes;
301cb0ef41Sopenharmony_ci}
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_cibool PCIsInCodeRange(const v8::MemoryRange& code_range, void* pc) {
331cb0ef41Sopenharmony_ci  return pc >= code_range.start &&
341cb0ef41Sopenharmony_ci         pc < CalculateEnd(code_range.start, code_range.length_in_bytes);
351cb0ef41Sopenharmony_ci}
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci// This relies on the fact that the code pages are ordered, and that they don't
381cb0ef41Sopenharmony_ci// overlap.
391cb0ef41Sopenharmony_cibool PCIsInCodePages(size_t code_pages_length, const MemoryRange* code_pages,
401cb0ef41Sopenharmony_ci                     void* pc) {
411cb0ef41Sopenharmony_ci  DCHECK(std::is_sorted(code_pages, code_pages + code_pages_length,
421cb0ef41Sopenharmony_ci                        [](const MemoryRange& a, const MemoryRange& b) {
431cb0ef41Sopenharmony_ci                          return a.start < b.start;
441cb0ef41Sopenharmony_ci                        }));
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ci  MemoryRange fake_range{pc, 1};
471cb0ef41Sopenharmony_ci  auto it =
481cb0ef41Sopenharmony_ci      std::upper_bound(code_pages, code_pages + code_pages_length, fake_range,
491cb0ef41Sopenharmony_ci                       [](const MemoryRange& a, const MemoryRange& b) {
501cb0ef41Sopenharmony_ci                         return a.start < b.start;
511cb0ef41Sopenharmony_ci                       });
521cb0ef41Sopenharmony_ci  DCHECK_IMPLIES(it != code_pages + code_pages_length, pc < it->start);
531cb0ef41Sopenharmony_ci  if (it == code_pages) return false;
541cb0ef41Sopenharmony_ci  --it;
551cb0ef41Sopenharmony_ci  return it->start <= pc && pc < CalculateEnd(it->start, it->length_in_bytes);
561cb0ef41Sopenharmony_ci}
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_cibool IsInJSEntryRange(const JSEntryStubs& entry_stubs, void* pc) {
591cb0ef41Sopenharmony_ci  return PCIsInCodeRange(entry_stubs.js_entry_stub.code, pc) ||
601cb0ef41Sopenharmony_ci         PCIsInCodeRange(entry_stubs.js_construct_entry_stub.code, pc) ||
611cb0ef41Sopenharmony_ci         PCIsInCodeRange(entry_stubs.js_run_microtasks_entry_stub.code, pc);
621cb0ef41Sopenharmony_ci}
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_cibool IsInUnsafeJSEntryRange(const JSEntryStubs& entry_stubs, void* pc) {
651cb0ef41Sopenharmony_ci  return IsInJSEntryRange(entry_stubs, pc);
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ci  // TODO(petermarshall): We can be more precise by checking whether we are
681cb0ef41Sopenharmony_ci  // in JSEntry but after frame setup and before frame teardown, in which case
691cb0ef41Sopenharmony_ci  // we are safe to unwind the stack. For now, we bail out if the PC is anywhere
701cb0ef41Sopenharmony_ci  // within JSEntry.
711cb0ef41Sopenharmony_ci}
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_cibool AddressIsInStack(const void* address, const void* stack_base,
741cb0ef41Sopenharmony_ci                      const void* stack_top) {
751cb0ef41Sopenharmony_ci  return address <= stack_base && address >= stack_top;
761cb0ef41Sopenharmony_ci}
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_civoid* GetReturnAddressFromFP(void* fp, void* pc,
791cb0ef41Sopenharmony_ci                             const JSEntryStubs& entry_stubs) {
801cb0ef41Sopenharmony_ci  int caller_pc_offset = i::CommonFrameConstants::kCallerPCOffset;
811cb0ef41Sopenharmony_ci// TODO(solanes): Implement the JSEntry range case also for x64 here and below.
821cb0ef41Sopenharmony_ci#if V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_ARM
831cb0ef41Sopenharmony_ci  if (IsInJSEntryRange(entry_stubs, pc)) {
841cb0ef41Sopenharmony_ci    caller_pc_offset = i::EntryFrameConstants::kDirectCallerPCOffset;
851cb0ef41Sopenharmony_ci  }
861cb0ef41Sopenharmony_ci#endif
871cb0ef41Sopenharmony_ci  i::Address ret_addr =
881cb0ef41Sopenharmony_ci      Load(reinterpret_cast<i::Address>(fp) + caller_pc_offset);
891cb0ef41Sopenharmony_ci  return reinterpret_cast<void*>(i::PointerAuthentication::StripPAC(ret_addr));
901cb0ef41Sopenharmony_ci}
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_civoid* GetCallerFPFromFP(void* fp, void* pc, const JSEntryStubs& entry_stubs) {
931cb0ef41Sopenharmony_ci  int caller_fp_offset = i::CommonFrameConstants::kCallerFPOffset;
941cb0ef41Sopenharmony_ci#if V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_ARM
951cb0ef41Sopenharmony_ci  if (IsInJSEntryRange(entry_stubs, pc)) {
961cb0ef41Sopenharmony_ci    caller_fp_offset = i::EntryFrameConstants::kDirectCallerFPOffset;
971cb0ef41Sopenharmony_ci  }
981cb0ef41Sopenharmony_ci#endif
991cb0ef41Sopenharmony_ci  return reinterpret_cast<void*>(
1001cb0ef41Sopenharmony_ci      Load(reinterpret_cast<i::Address>(fp) + caller_fp_offset));
1011cb0ef41Sopenharmony_ci}
1021cb0ef41Sopenharmony_ci
1031cb0ef41Sopenharmony_civoid* GetCallerSPFromFP(void* fp, void* pc, const JSEntryStubs& entry_stubs) {
1041cb0ef41Sopenharmony_ci  int caller_sp_offset = i::CommonFrameConstants::kCallerSPOffset;
1051cb0ef41Sopenharmony_ci#if V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_ARM
1061cb0ef41Sopenharmony_ci  if (IsInJSEntryRange(entry_stubs, pc)) {
1071cb0ef41Sopenharmony_ci    caller_sp_offset = i::EntryFrameConstants::kDirectCallerSPOffset;
1081cb0ef41Sopenharmony_ci  }
1091cb0ef41Sopenharmony_ci#endif
1101cb0ef41Sopenharmony_ci  return reinterpret_cast<void*>(reinterpret_cast<i::Address>(fp) +
1111cb0ef41Sopenharmony_ci                                 caller_sp_offset);
1121cb0ef41Sopenharmony_ci}
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci}  // namespace
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_cibool Unwinder::TryUnwindV8Frames(const JSEntryStubs& entry_stubs,
1171cb0ef41Sopenharmony_ci                                 size_t code_pages_length,
1181cb0ef41Sopenharmony_ci                                 const MemoryRange* code_pages,
1191cb0ef41Sopenharmony_ci                                 RegisterState* register_state,
1201cb0ef41Sopenharmony_ci                                 const void* stack_base) {
1211cb0ef41Sopenharmony_ci  const void* stack_top = register_state->sp;
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_ci  void* pc = register_state->pc;
1241cb0ef41Sopenharmony_ci  if (PCIsInV8(code_pages_length, code_pages, pc) &&
1251cb0ef41Sopenharmony_ci      !IsInUnsafeJSEntryRange(entry_stubs, pc)) {
1261cb0ef41Sopenharmony_ci    void* current_fp = register_state->fp;
1271cb0ef41Sopenharmony_ci    if (!AddressIsInStack(current_fp, stack_base, stack_top)) return false;
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci    // Peek at the return address that the caller pushed. If it's in V8, then we
1301cb0ef41Sopenharmony_ci    // assume the caller frame is a JS frame and continue to unwind.
1311cb0ef41Sopenharmony_ci    void* next_pc = GetReturnAddressFromFP(current_fp, pc, entry_stubs);
1321cb0ef41Sopenharmony_ci    while (PCIsInV8(code_pages_length, code_pages, next_pc)) {
1331cb0ef41Sopenharmony_ci      current_fp = GetCallerFPFromFP(current_fp, pc, entry_stubs);
1341cb0ef41Sopenharmony_ci      if (!AddressIsInStack(current_fp, stack_base, stack_top)) return false;
1351cb0ef41Sopenharmony_ci      pc = next_pc;
1361cb0ef41Sopenharmony_ci      next_pc = GetReturnAddressFromFP(current_fp, pc, entry_stubs);
1371cb0ef41Sopenharmony_ci    }
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci    void* final_sp = GetCallerSPFromFP(current_fp, pc, entry_stubs);
1401cb0ef41Sopenharmony_ci    if (!AddressIsInStack(final_sp, stack_base, stack_top)) return false;
1411cb0ef41Sopenharmony_ci    register_state->sp = final_sp;
1421cb0ef41Sopenharmony_ci
1431cb0ef41Sopenharmony_ci    // We don't check that the final FP value is within the stack bounds because
1441cb0ef41Sopenharmony_ci    // this is just the rbp value that JSEntryStub pushed. On platforms like
1451cb0ef41Sopenharmony_ci    // Win64 this is not used as a dedicated FP register, and could contain
1461cb0ef41Sopenharmony_ci    // anything.
1471cb0ef41Sopenharmony_ci    void* final_fp = GetCallerFPFromFP(current_fp, pc, entry_stubs);
1481cb0ef41Sopenharmony_ci    register_state->fp = final_fp;
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_ci    register_state->pc = next_pc;
1511cb0ef41Sopenharmony_ci
1521cb0ef41Sopenharmony_ci    // Link register no longer valid after unwinding.
1531cb0ef41Sopenharmony_ci    register_state->lr = nullptr;
1541cb0ef41Sopenharmony_ci
1551cb0ef41Sopenharmony_ci    if (IsInJSEntryRange(entry_stubs, pc)) {
1561cb0ef41Sopenharmony_ci      GetCalleeSavedRegistersFromEntryFrame(current_fp, register_state);
1571cb0ef41Sopenharmony_ci    }
1581cb0ef41Sopenharmony_ci    return true;
1591cb0ef41Sopenharmony_ci  }
1601cb0ef41Sopenharmony_ci  return false;
1611cb0ef41Sopenharmony_ci}
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_cibool Unwinder::PCIsInV8(size_t code_pages_length, const MemoryRange* code_pages,
1641cb0ef41Sopenharmony_ci                        void* pc) {
1651cb0ef41Sopenharmony_ci  return pc && PCIsInCodePages(code_pages_length, code_pages, pc);
1661cb0ef41Sopenharmony_ci}
1671cb0ef41Sopenharmony_ci
1681cb0ef41Sopenharmony_ci}  // namespace v8
169