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