diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index 201f1e48f1fb3..f3681de00ba03 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -856,7 +856,7 @@ void LinkerScript::switchTo(OutputSection *sec) { // ctx->outSec->alignment is the max of ALIGN and the maximum of input // section alignments. ctx->outSec->addr = advance(0, ctx->outSec->alignment); - expandMemoryRegions(ctx->outSec->addr - pos); + expandMemoryRegions(ctx->outSec->addr - pos);// TODO : check if this doesn't require some modification for overlay case } } @@ -908,6 +908,10 @@ void LinkerScript::assignOffsets(OutputSection *sec) { ctx->memRegion = sec->memRegion; ctx->lmaRegion = sec->lmaRegion; + if (sec->inOverlay && ctx->inOverlay && sec->addrExpr().getValue() == ctx->outSec->addrExpr().getValue()) + ctx->memRegion->curPos -= ctx->outSec->size; + ctx->inOverlay = sec->inOverlay; + if (sec->flags & SHF_ALLOC) { if (ctx->memRegion) dot = ctx->memRegion->curPos; diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h index efa473f45e308..d54fe8253b222 100644 --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -252,6 +252,7 @@ class LinkerScript final { MemoryRegion *memRegion = nullptr; MemoryRegion *lmaRegion = nullptr; uint64_t lmaOffset = 0; + bool inOverlay = false; }; llvm::DenseMap nameToOutputSection; diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp index b81812d11821e..65858bb90a2ab 100644 --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -526,13 +526,18 @@ std::vector ScriptParser::readOverlay() { OutputSection *os = readOverlaySectionDescription(); os->addrExpr = addrExpr; if (prev) - os->lmaExpr = [=] { return prev->getLMA() + prev->size; }; + os->lmaExpr = [=] { return prev->size? prev->getLMA() + prev->size : prev->lmaExpr(); }; else os->lmaExpr = lmaExpr; v.push_back(os); prev = os; + // TODO : make sure os->name is sanitized ("." -> "_") before being used in symbols + v.push_back(make(saver.save("__load_start_" + os->name), os->lmaExpr, getCurrentLocation())); + Expr load_stop = [=] { return os->size? os->getLMA() + os->size : os->lmaExpr(); }; + v.push_back(make(saver.save("__load_stop_" + os->name), load_stop, getCurrentLocation())); } + // According to the specification, at the end of the overlay, the location // counter should be equal to the overlay base address plus size of the // largest section seen in the overlay. diff --git a/lldb/source/Plugins/Process/Dpu/Dpu.cpp b/lldb/source/Plugins/Process/Dpu/Dpu.cpp index d18fa50123713..16e5835340c92 100644 --- a/lldb/source/Plugins/Process/Dpu/Dpu.cpp +++ b/lldb/source/Plugins/Process/Dpu/Dpu.cpp @@ -678,6 +678,15 @@ bool Dpu::ReadMRAM(uint32_t offset, void *buf, size_t size) { return ret == DPU_OK; } +bool Dpu::GetSymbol(const char *symbol_name, dpu_symbol_t *symbol) { + dpu_program_t *runtime = dpu_get_program(m_dpu); + lldbassert(runtime); + if (!runtime) + return false; + if (dpu_get_symbol(runtime, symbol_name, symbol) == DPU_OK) return true; + return false; +} + bool Dpu::AllocIRAMBuffer(uint8_t **iram, uint32_t *iram_size) { dpu_description_t description = dpu_get_description(dpu_get_rank(m_dpu)); uint32_t nb_instructions = description->hw.memories.iram_size; diff --git a/lldb/source/Plugins/Process/Dpu/Dpu.h b/lldb/source/Plugins/Process/Dpu/Dpu.h index c9877093f8d10..e2701e1a2b03a 100644 --- a/lldb/source/Plugins/Process/Dpu/Dpu.h +++ b/lldb/source/Plugins/Process/Dpu/Dpu.h @@ -81,6 +81,8 @@ class Dpu { bool WriteMRAM(uint32_t offset, const void *buf, size_t size); bool ReadMRAM(uint32_t offset, void *buf, size_t size); + bool GetSymbol(const char *symbol_name, dpu_symbol_t *symbol); + bool AllocIRAMBuffer(uint8_t **iram, uint32_t *iram_size); bool FreeIRAMBuffer(uint8_t *iram); bool GenerateSaveCore(const char *exe_path, const char *core_file_path, diff --git a/lldb/source/Plugins/Process/Dpu/DpuContext.cpp b/lldb/source/Plugins/Process/Dpu/DpuContext.cpp index e1cd9f51391b7..20ede2803e32d 100644 --- a/lldb/source/Plugins/Process/Dpu/DpuContext.cpp +++ b/lldb/source/Plugins/Process/Dpu/DpuContext.cpp @@ -141,7 +141,8 @@ unsigned int DpuContext::GetExitStatus() { } lldb::addr_t DpuContext::GetPcOfThread(dpu_thread_t thread) { - return InstIdx2InstAddr(m_context->pcs[thread]); + uint32_t raw_pc = m_context->pcs[thread]; + return InstIdx2InstAddr(raw_pc); } bool DpuContext::ScheduledThread(uint32_t thread) { diff --git a/lldb/source/Plugins/Process/Dpu/ProcessDpu.cpp b/lldb/source/Plugins/Process/Dpu/ProcessDpu.cpp index e906276fee42a..f4daae2123da8 100644 --- a/lldb/source/Plugins/Process/Dpu/ProcessDpu.cpp +++ b/lldb/source/Plugins/Process/Dpu/ProcessDpu.cpp @@ -50,6 +50,10 @@ #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "ThreadDpu.h" +extern "C" { +#include +} + #include #include /* TODO only exists on Linux */ #include @@ -351,7 +355,7 @@ ProcessDpu::ProcessDpu(::pid_t pid, int terminal_fd, NativeDelegate &delegate, SetState(StateType::eStateStopped, false); m_iram_region.GetRange().SetRangeBase(k_dpu_iram_base); - m_iram_region.GetRange().SetRangeEnd(k_dpu_iram_base + m_iram_size); + m_iram_region.GetRange().SetRangeEnd(k_dpu_iram_base + k_dpu_viram_offset*6 + m_iram_size); m_iram_region.SetReadable(MemoryRegionInfo::eYes); m_iram_region.SetWritable(MemoryRegionInfo::eYes); m_iram_region.SetExecutable(MemoryRegionInfo::eYes); @@ -374,6 +378,8 @@ ProcessDpu::ProcessDpu(::pid_t pid, int terminal_fd, NativeDelegate &delegate, void ProcessDpu::InterfaceTimerCallback() { unsigned int exit_status; + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + LLDB_LOG(log, "========= this is a log test ==========="); StateType current_state = m_dpu->PollStatus(&exit_status); if (current_state != StateType::eStateInvalid) { if (current_state == StateType::eStateExited) { @@ -546,6 +552,7 @@ Status ProcessDpu::GetMemoryRegionInfo(lldb::addr_t load_addr, range_info = m_mram_region; } else if (m_iram_region.GetRange().Contains(load_addr)) { range_info = m_iram_region; + // FIXME : add viram ranges } else { range_info.GetRange().SetRangeBase(load_addr); range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); @@ -578,8 +585,10 @@ size_t ProcessDpu::UpdateThreads() { return m_threads.size(); } Status ProcessDpu::SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) { + size_t bytes_read; + Log *log(GetLogIfAnyCategoriesSet(POSIX_LOG_BREAKPOINTS)); if (hardware) - return SetHardwareBreakpoint(addr, size); + return SetHardwareBreakpoint(addr, size); else return SetSoftwareBreakpoint(addr, size); } @@ -593,7 +602,9 @@ Status ProcessDpu::RemoveBreakpoint(lldb::addr_t addr, bool hardware) { ProcessDpu::eMemoryAddressSpace ProcessDpu::ComputeMemoryAddressSpace(lldb::addr_t addr, size_t size) { - if (addr >= k_dpu_iram_base && + if (addr >= k_dpu_iram_base + k_dpu_viram_offset && ((addr & (~k_dpu_viram_msb_mask)) + size) <= (k_dpu_iram_base + m_iram_size)) { + return eMemoryAddressSpaceVIRAM; + } else if (addr >= k_dpu_iram_base && (addr + size) <= (k_dpu_iram_base + m_iram_size)) { return eMemoryAddressSpaceIRAM; } else if (addr >= k_dpu_mram_base && @@ -610,10 +621,20 @@ ProcessDpu::ComputeMemoryAddressSpace(lldb::addr_t addr, size_t size) { Status ProcessDpu::ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); + Status error; LLDB_LOG(log, "addr = {0:X}, buf = {1}, size = {2}", addr, buf, size); bytes_read = 0; switch (ComputeMemoryAddressSpace(addr, size)) { + case eMemoryAddressSpaceVIRAM: + // expected behaviour : if can fetch symbols, read correct mram location. Otherwise, read iram + lldb::addr_t mram_addr; + error = ViramAddressToMramPhysicalAddress(addr, &mram_addr); + if (error.Fail()) + return Status("ReadMemory: cannot convert viram to mram physical address"); + if (!m_dpu->ReadMRAM(mram_addr - k_dpu_mram_base, buf, size)) + return Status("ReadMemory: Cannot copy from MRAM"); + break; case eMemoryAddressSpaceIRAM: if (!m_dpu->ReadIRAM(addr - k_dpu_iram_base, buf, size)) return Status("ReadMemory: Cannot copy from IRAM"); @@ -640,10 +661,26 @@ Status ProcessDpu::ReadMemory(lldb::addr_t addr, void *buf, size_t size, Status ProcessDpu::WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); + Status error; LLDB_LOG(log, "addr = {0:X}, buf = {1}, size = {2}", addr, buf, size); bytes_written = 0; switch (ComputeMemoryAddressSpace(addr, size)) { + case eMemoryAddressSpaceVIRAM: + // expected behaviour : if can fetch symbols, write to correct mram location and if fg is loaded, write to iram. Otherwise, only write to iram + lldb::addr_t mram_addr; + lldb::addr_t iram_addr; + error = ViramAddressToMramPhysicalAddress(addr, &mram_addr); + if (error.Fail()) + return Status("WriteMemory: cannot convert viram to mram physical address"); + if (!m_dpu->WriteMRAM(mram_addr - k_dpu_mram_base, buf, size)) + return Status("WriteMemory: Cannot copy to MRAM"); + error = ViramAddressToLoadedIramAddress(addr, &iram_addr); + if (error.Fail()) { + LLDB_LOG(log, "WriteMemory: Could not write viram to iram as this function group is not currently loaded"); + } else if (!m_dpu->WriteIRAM(iram_addr - k_dpu_iram_base, buf, size)) + return Status("WriteMemory: Cannot copy to IRAM"); + break; case eMemoryAddressSpaceIRAM: if (!m_dpu->WriteIRAM(addr - k_dpu_iram_base, buf, size)) return Status("WriteMemory: Cannot copy to IRAM"); @@ -667,6 +704,57 @@ Status ProcessDpu::WriteMemory(lldb::addr_t addr, const void *buf, size_t size, return Status(); } +Status ProcessDpu::ViramAddressToMramPhysicalAddress(lldb::addr_t viram_addr, lldb::addr_t *mram_addr) { + Status error; + size_t bytes_read; + lldb::addr_t overlay_start_address = 0; + lldb::addr_t load_starts_table_address = 0; + lldb::addr_t load_start_address = 0; + size_t fg_id = ((viram_addr & k_dpu_viram_msb_mask)/k_dpu_viram_offset) - 1; + const char *using_function_groups = std::getenv("USING_FUNCTION_GROUPS"); + if (using_function_groups == NULL || using_function_groups[0] == '\0') + return Status("Could not find USING_FUNCTION_GROUPS env variable\n"); + error = ReadMemory(ADDR_FG_IRAM_OVERLAY_START, &overlay_start_address, 4, bytes_read); + if(error.Fail() || bytes_read != 4) + return Status("could not read load start address\n"); + overlay_start_address <<= 3; + lldbassert(viram_addr >= overlay_start_address); + error = ReadMemory(ADDR_FG_LOAD_STARTS_ADDR, &load_starts_table_address, 4, bytes_read); + if(error.Fail() || bytes_read != 4) + return Status("could not read load starts table address\n"); + if (load_starts_table_address == 0) + return Status("function groups not properly initialized"); + error = ReadMemory(load_starts_table_address+4*fg_id, &load_start_address, 4, bytes_read); + if(error.Fail() || bytes_read != 4) + return Status("could not read load start address\n"); + *mram_addr = (viram_addr & ~k_dpu_viram_msb_mask) + load_start_address - overlay_start_address - k_dpu_iram_base + k_dpu_mram_base; + return Status(); +} + +Status ProcessDpu::ViramAddressToLoadedIramAddress(lldb::addr_t viram_addr, lldb::addr_t *iram_addr) { + Status error; + size_t bytes_read; + lldb::addr_t overlay_start_address; + // FIXME : try and fetch symbol instead of trusting overlay_start_address will always be stored at the same address in the elf + error = ReadMemory(ADDR_FG_IRAM_OVERLAY_START, &overlay_start_address, 4, bytes_read); + if(!error.Fail() && bytes_read == 4) { + overlay_start_address <<= 3; + overlay_start_address += 0x80000000; + if (viram_addr >= overlay_start_address && overlay_start_address != 0x80000000) { + uint32_t loaded_group_value; + // FIXME : try and fetch symbol instead of trusting the loaded_group will always be stored at the same address in the elf + error = ReadMemory(ADDR_FG_CURRENTLY_LOADED_GROUP, &loaded_group_value, 4, bytes_read); + if(!error.Fail() && bytes_read == 4) { + if ((viram_addr & k_dpu_viram_msb_mask) == k_dpu_viram_offset*(loaded_group_value+1)) { + *iram_addr = viram_addr & ~k_dpu_viram_msb_mask; + return Status(); + } + } + } + } + return Status("This function group is not currently loaded in iram\n"); +} + Status ProcessDpu::GetLoadedModuleFileSpec(const char *module_path, FileSpec &file_spec) { return DpuErrorStatus("GetLoadedModuleFileSpec: Not Implemented"); diff --git a/lldb/source/Plugins/Process/Dpu/ProcessDpu.h b/lldb/source/Plugins/Process/Dpu/ProcessDpu.h index 0a03ab6cd77a2..818a86ca75fb1 100644 --- a/lldb/source/Plugins/Process/Dpu/ProcessDpu.h +++ b/lldb/source/Plugins/Process/Dpu/ProcessDpu.h @@ -25,6 +25,11 @@ #include "ThreadDpu.h" #include "lldb/Host/common/NativeProcessProtocol.h" +#define ADDR_FG_IRAM_OVERLAY_START 0x8 +#define ADDR_FG_CURRENTLY_LOADED_GROUP 0x10 +#define ADDR_FG_LOAD_STARTS_ADDR 0x18 +#define ADDR_FG_INITIALIZED 0x20 + namespace lldb_private { class Status; class Scalar; @@ -39,6 +44,8 @@ const ArchSpec k_dpu_arch("dpu-upmem-dpurte"); constexpr lldb::addr_t k_dpu_wram_base = 0x00000000; constexpr lldb::addr_t k_dpu_mram_base = 0x08000000; constexpr lldb::addr_t k_dpu_iram_base = 0x80000000; +constexpr lldb::addr_t k_dpu_viram_offset = 0x00100000; +constexpr lldb::addr_t k_dpu_viram_msb_mask = k_dpu_viram_offset*0xf; } // namespace dpu namespace process_dpu { @@ -96,6 +103,10 @@ class ProcessDpu : public NativeProcessProtocol { Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override; + Status ViramAddressToMramPhysicalAddress(lldb::addr_t viram_addr, lldb::addr_t *mram_addr); + + Status ViramAddressToLoadedIramAddress(lldb::addr_t viram_addr, lldb::addr_t *iram_addr); + virtual llvm::Expected AllocateMemory(size_t size, uint32_t permissions) override; @@ -167,6 +178,7 @@ class ProcessDpu : public NativeProcessProtocol { Status DpuErrorStatus(const char *message); enum eMemoryAddressSpace { + eMemoryAddressSpaceVIRAM, eMemoryAddressSpaceIRAM, eMemoryAddressSpaceMRAM, eMemoryAddressSpaceWRAM, diff --git a/lldb/source/Plugins/Process/Dpu/RegisterContextDpu.cpp b/lldb/source/Plugins/Process/Dpu/RegisterContextDpu.cpp index edbbe7d8f15b9..9c2a9c0de0b39 100644 --- a/lldb/source/Plugins/Process/Dpu/RegisterContextDpu.cpp +++ b/lldb/source/Plugins/Process/Dpu/RegisterContextDpu.cpp @@ -28,6 +28,7 @@ using namespace lldb_private::process_dpu; namespace { constexpr lldb::addr_t k_dpu_iram_base = 0x80000000; +constexpr lldb::addr_t k_dpu_viram_offset = 0x00100000; } // end of anonymous namespace @@ -78,6 +79,7 @@ RegisterContextDpu::RegisterContextDpu(ThreadDpu &thread, ProcessDpu &process) process.GetThreadContext(thread.GetIndex(), m_context_reg, m_context_pc, m_context_zf, m_context_cf, m_registers_has_been_modified); + m_process = &process; } uint32_t RegisterContextDpu::GetRegisterSetCount() const { return 1; } @@ -94,6 +96,38 @@ RegisterContextDpu::GetRegisterSet(uint32_t set_index) const { return &g_reg_sets_dpu; } +Status FixPc(ProcessDpu *process, uint32_t *pc) { + Status error; + size_t bytes_read; + // uint64_t magic_value; + uint32_t initialized = 0; + lldb::addr_t overlay_start_address = 0; + uint32_t fg_id; + + const char *using_function_groups = std::getenv("USING_FUNCTION_GROUPS"); + if (using_function_groups == NULL || using_function_groups[0] == '\0') + return Status("Could not find USING_FUNCTION_GROUPS env variable\n"); + + error = process->ReadMemory(ADDR_FG_INITIALIZED, &initialized, 4, bytes_read); + if(error.Fail() || bytes_read != 4 || initialized == 0) + return Status("fg groups not initialized\n"); + + error = process->ReadMemory(ADDR_FG_IRAM_OVERLAY_START, &overlay_start_address, 4, bytes_read); + if(error.Fail() || bytes_read != 4) + return Status("could not read load start address\n"); + + overlay_start_address <<= 3; + if((*pc) < k_dpu_iram_base + overlay_start_address) + return Status("pc below overlay_start_address\n"); + + error = process->ReadMemory(ADDR_FG_CURRENTLY_LOADED_GROUP, &fg_id, 4, bytes_read); + if(error.Fail() || bytes_read != 4) + return Status("could not read load start address\n"); + + *pc |= k_dpu_viram_offset * (1 + fg_id); + return Status(); +} + Status RegisterContextDpu::ReadRegister(const RegisterInfo *info, RegisterValue &value) { if (!info) @@ -106,6 +140,7 @@ Status RegisterContextDpu::ReadRegister(const RegisterInfo *info, *m_context_pc * 8 /*sizeof(iram_instruction_t)*/ + k_dpu_iram_base; if (m_thread.GetState() == eStateSuspended) pc++; + FixPc(m_process, &pc); value.SetUInt32(pc); } else if (reg == zf_dpu) value.SetUInt32(*m_context_zf ? 1 : 0); diff --git a/lldb/source/Plugins/Process/Dpu/RegisterContextDpu.h b/lldb/source/Plugins/Process/Dpu/RegisterContextDpu.h index 97aeb4caed9b8..6d4f443496283 100644 --- a/lldb/source/Plugins/Process/Dpu/RegisterContextDpu.h +++ b/lldb/source/Plugins/Process/Dpu/RegisterContextDpu.h @@ -46,6 +46,7 @@ class RegisterContextDpu : public NativeRegisterContextRegisterInfo { bool *m_context_zf; bool *m_context_cf; bool *m_registers_has_been_modified; + ProcessDpu *m_process; }; } // namespace process_dpu diff --git a/lldb/source/Plugins/Process/Utility/UnwindDPU.cpp b/lldb/source/Plugins/Process/Utility/UnwindDPU.cpp index f95186db51602..5e61fa6a881c3 100644 --- a/lldb/source/Plugins/Process/Utility/UnwindDPU.cpp +++ b/lldb/source/Plugins/Process/Utility/UnwindDPU.cpp @@ -25,11 +25,16 @@ using namespace lldb; using namespace lldb_private; +#define ADDR_FG_IRAM_OVERLAY_START 0x8 +#define ADDR_FG_CURRENTLY_LOADED_GROUP 0x10 +#define DPU_IRAM_BASE 0x80000000 +#define FG_DPU_VIRAM_OFFSET 0x00100000 + UnwindDPU::UnwindDPU(Thread &thread) : Unwind(thread), m_frames() {} void UnwindDPU::DoClear() { m_frames.clear(); } -#define FORMAT_PC(pc) (0x80000000 | ((pc)*8)) +#define FORMAT_PC(pc) (DPU_IRAM_BASE | ((pc)*8)) bool UnwindDPU::SetFrame(CursorSP *prev_frame, lldb::addr_t cfa, lldb::addr_t pc, Address &fct_base_addr) { @@ -129,6 +134,29 @@ uint32_t UnwindDPU::DoGetFrameCount() { return m_frames.size(); } +bool UnwindDPU::FixPcForOverlay(lldb::addr_t &pc) { + Status error; + lldb::addr_t overlay_start_address; + lldb::addr_t viram_bitmask = 0; + const char *using_function_groups = std::getenv("USING_FUNCTION_GROUPS"); + + if (using_function_groups == NULL || using_function_groups[0] == '\0') + return true; + + if(m_thread.GetProcess()->ReadMemory(ADDR_FG_IRAM_OVERLAY_START, &overlay_start_address, 8, error) != 8) + return false; + overlay_start_address = FORMAT_PC(overlay_start_address); + if (pc >= overlay_start_address && overlay_start_address != DPU_IRAM_BASE) { + uint32_t loaded_group_value; + // FIXME : try and fetch symbol instead of trusting the loaded_group will always be stored at the same address in the elf + if(m_thread.GetProcess()->ReadMemory(ADDR_FG_CURRENTLY_LOADED_GROUP, &loaded_group_value, 4, error) != 4) + return false; + viram_bitmask = FG_DPU_VIRAM_OFFSET*(loaded_group_value+1); + } + pc |= viram_bitmask; + return true; +} + bool UnwindDPU::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, lldb::addr_t &pc, bool &behaves_like_zeroth_frame) { if (frame_idx >= DoGetFrameCount()) @@ -137,7 +165,8 @@ bool UnwindDPU::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, behaves_like_zeroth_frame = frame_idx == 0; cfa = m_frames[frame_idx]->cfa; pc = m_frames[frame_idx]->pc; - return true; + + return FixPcForOverlay(pc); } lldb::RegisterContextSP diff --git a/lldb/source/Plugins/Process/Utility/UnwindDPU.h b/lldb/source/Plugins/Process/Utility/UnwindDPU.h index dd6f622e84964..2a9dfaf9c66ac 100644 --- a/lldb/source/Plugins/Process/Utility/UnwindDPU.h +++ b/lldb/source/Plugins/Process/Utility/UnwindDPU.h @@ -39,6 +39,8 @@ class UnwindDPU : public lldb_private::Unwind { uint32_t DoGetFrameCount() override; + bool FixPcForOverlay(lldb::addr_t &pc); + bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, lldb::addr_t &pc, bool &behaves_like_zeroth_frame) override;