| //===-- UnwindMacOSXFrameBackchain.cpp --------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Symbol/Function.h" |
| #include "lldb/Symbol/ObjectFile.h" |
| #include "lldb/Symbol/Symbol.h" |
| #include "lldb/Target/ExecutionContext.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Target/Thread.h" |
| #include "lldb/Utility/ArchSpec.h" |
| |
| #include "RegisterContextMacOSXFrameBackchain.h" |
| |
| #include <memory> |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| UnwindMacOSXFrameBackchain::UnwindMacOSXFrameBackchain(Thread &thread) |
| : Unwind(thread), m_cursors() {} |
| |
| uint32_t UnwindMacOSXFrameBackchain::DoGetFrameCount() { |
| if (m_cursors.empty()) { |
| ExecutionContext exe_ctx(m_thread.shared_from_this()); |
| Target *target = exe_ctx.GetTargetPtr(); |
| if (target) { |
| const ArchSpec &target_arch = target->GetArchitecture(); |
| // Frame zero should always be supplied by the thread... |
| exe_ctx.SetFrameSP(m_thread.GetStackFrameAtIndex(0)); |
| |
| if (target_arch.GetAddressByteSize() == 8) |
| GetStackFrameData_x86_64(exe_ctx); |
| else |
| GetStackFrameData_i386(exe_ctx); |
| } |
| } |
| return m_cursors.size(); |
| } |
| |
| bool UnwindMacOSXFrameBackchain::DoGetFrameInfoAtIndex(uint32_t idx, |
| addr_t &cfa, |
| addr_t &pc) { |
| const uint32_t frame_count = GetFrameCount(); |
| if (idx < frame_count) { |
| if (m_cursors[idx].pc == LLDB_INVALID_ADDRESS) |
| return false; |
| if (m_cursors[idx].fp == LLDB_INVALID_ADDRESS) |
| return false; |
| |
| pc = m_cursors[idx].pc; |
| cfa = m_cursors[idx].fp; |
| |
| return true; |
| } |
| return false; |
| } |
| |
| lldb::RegisterContextSP |
| UnwindMacOSXFrameBackchain::DoCreateRegisterContextForFrame(StackFrame *frame) { |
| lldb::RegisterContextSP reg_ctx_sp; |
| uint32_t concrete_idx = frame->GetConcreteFrameIndex(); |
| const uint32_t frame_count = GetFrameCount(); |
| if (concrete_idx < frame_count) |
| reg_ctx_sp = std::make_shared<RegisterContextMacOSXFrameBackchain>( |
| m_thread, concrete_idx, m_cursors[concrete_idx]); |
| return reg_ctx_sp; |
| } |
| |
| size_t UnwindMacOSXFrameBackchain::GetStackFrameData_i386( |
| const ExecutionContext &exe_ctx) { |
| m_cursors.clear(); |
| |
| StackFrame *first_frame = exe_ctx.GetFramePtr(); |
| |
| Process *process = exe_ctx.GetProcessPtr(); |
| if (process == nullptr) |
| return 0; |
| |
| struct Frame_i386 { |
| uint32_t fp; |
| uint32_t pc; |
| }; |
| |
| RegisterContext *reg_ctx = m_thread.GetRegisterContext().get(); |
| assert(reg_ctx); |
| |
| Cursor cursor; |
| cursor.pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS); |
| cursor.fp = reg_ctx->GetFP(0); |
| |
| Frame_i386 frame = {static_cast<uint32_t>(cursor.fp), |
| static_cast<uint32_t>(cursor.pc)}; |
| |
| m_cursors.push_back(cursor); |
| |
| const size_t k_frame_size = sizeof(frame); |
| Status error; |
| while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) { |
| // Read both the FP and PC (8 bytes) |
| if (process->ReadMemory(frame.fp, &frame.fp, k_frame_size, error) != |
| k_frame_size) |
| break; |
| if (frame.pc >= 0x1000) { |
| cursor.pc = frame.pc; |
| cursor.fp = frame.fp; |
| m_cursors.push_back(cursor); |
| } |
| } |
| if (!m_cursors.empty()) { |
| lldb::addr_t first_frame_pc = m_cursors.front().pc; |
| if (first_frame_pc != LLDB_INVALID_ADDRESS) { |
| const SymbolContextItem resolve_scope = |
| eSymbolContextModule | eSymbolContextCompUnit | |
| eSymbolContextFunction | eSymbolContextSymbol; |
| |
| SymbolContext first_frame_sc( |
| first_frame->GetSymbolContext(resolve_scope)); |
| const AddressRange *addr_range_ptr = nullptr; |
| AddressRange range; |
| if (first_frame_sc.function) |
| addr_range_ptr = &first_frame_sc.function->GetAddressRange(); |
| else if (first_frame_sc.symbol) { |
| range.GetBaseAddress() = first_frame_sc.symbol->GetAddress(); |
| range.SetByteSize(first_frame_sc.symbol->GetByteSize()); |
| addr_range_ptr = ⦥ |
| } |
| |
| if (addr_range_ptr) { |
| if (first_frame->GetFrameCodeAddress() == |
| addr_range_ptr->GetBaseAddress()) { |
| // We are at the first instruction, so we can recover the previous PC |
| // by dereferencing the SP |
| lldb::addr_t first_frame_sp = reg_ctx->GetSP(0); |
| // Read the real second frame return address into frame.pc |
| if (first_frame_sp && |
| process->ReadMemory(first_frame_sp, &frame.pc, sizeof(frame.pc), |
| error) == sizeof(frame.pc)) { |
| cursor.fp = m_cursors.front().fp; |
| cursor.pc = frame.pc; // Set the new second frame PC |
| |
| // Insert the second frame |
| m_cursors.insert(m_cursors.begin() + 1, cursor); |
| |
| m_cursors.front().fp = first_frame_sp; |
| } |
| } |
| } |
| } |
| } |
| // uint32_t i=0; |
| // printf(" PC FP\n"); |
| // printf(" ------------------ ------------------ \n"); |
| // for (i=0; i<m_cursors.size(); ++i) |
| // { |
| // printf("[%3u] 0x%16.16" PRIx64 " 0x%16.16" PRIx64 "\n", i, |
| // m_cursors[i].pc, m_cursors[i].fp); |
| // } |
| return m_cursors.size(); |
| } |
| |
| size_t UnwindMacOSXFrameBackchain::GetStackFrameData_x86_64( |
| const ExecutionContext &exe_ctx) { |
| m_cursors.clear(); |
| |
| Process *process = exe_ctx.GetProcessPtr(); |
| if (process == nullptr) |
| return 0; |
| |
| StackFrame *first_frame = exe_ctx.GetFramePtr(); |
| |
| struct Frame_x86_64 { |
| uint64_t fp; |
| uint64_t pc; |
| }; |
| |
| RegisterContext *reg_ctx = m_thread.GetRegisterContext().get(); |
| assert(reg_ctx); |
| |
| Cursor cursor; |
| cursor.pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS); |
| cursor.fp = reg_ctx->GetFP(0); |
| |
| Frame_x86_64 frame = {cursor.fp, cursor.pc}; |
| |
| m_cursors.push_back(cursor); |
| Status error; |
| const size_t k_frame_size = sizeof(frame); |
| while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) { |
| // Read both the FP and PC (16 bytes) |
| if (process->ReadMemory(frame.fp, &frame.fp, k_frame_size, error) != |
| k_frame_size) |
| break; |
| |
| if (frame.pc >= 0x1000) { |
| cursor.pc = frame.pc; |
| cursor.fp = frame.fp; |
| m_cursors.push_back(cursor); |
| } |
| } |
| if (!m_cursors.empty()) { |
| lldb::addr_t first_frame_pc = m_cursors.front().pc; |
| if (first_frame_pc != LLDB_INVALID_ADDRESS) { |
| const SymbolContextItem resolve_scope = |
| eSymbolContextModule | eSymbolContextCompUnit | |
| eSymbolContextFunction | eSymbolContextSymbol; |
| |
| SymbolContext first_frame_sc( |
| first_frame->GetSymbolContext(resolve_scope)); |
| const AddressRange *addr_range_ptr = nullptr; |
| AddressRange range; |
| if (first_frame_sc.function) |
| addr_range_ptr = &first_frame_sc.function->GetAddressRange(); |
| else if (first_frame_sc.symbol) { |
| range.GetBaseAddress() = first_frame_sc.symbol->GetAddress(); |
| range.SetByteSize(first_frame_sc.symbol->GetByteSize()); |
| addr_range_ptr = ⦥ |
| } |
| |
| if (addr_range_ptr) { |
| if (first_frame->GetFrameCodeAddress() == |
| addr_range_ptr->GetBaseAddress()) { |
| // We are at the first instruction, so we can recover the previous PC |
| // by dereferencing the SP |
| lldb::addr_t first_frame_sp = reg_ctx->GetSP(0); |
| // Read the real second frame return address into frame.pc |
| if (process->ReadMemory(first_frame_sp, &frame.pc, sizeof(frame.pc), |
| error) == sizeof(frame.pc)) { |
| cursor.fp = m_cursors.front().fp; |
| cursor.pc = frame.pc; // Set the new second frame PC |
| |
| // Insert the second frame |
| m_cursors.insert(m_cursors.begin() + 1, cursor); |
| |
| m_cursors.front().fp = first_frame_sp; |
| } |
| } |
| } |
| } |
| } |
| return m_cursors.size(); |
| } |