Path: blob/main/contrib/llvm-project/lldb/source/Symbol/UnwindPlan.cpp
39587 views
//===-- UnwindPlan.cpp ----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Symbol/UnwindPlan.h"910#include "lldb/Target/Process.h"11#include "lldb/Target/RegisterContext.h"12#include "lldb/Target/Target.h"13#include "lldb/Target/Thread.h"14#include "lldb/Utility/ConstString.h"15#include "lldb/Utility/LLDBLog.h"16#include "lldb/Utility/Log.h"17#include "llvm/DebugInfo/DIContext.h"18#include "llvm/DebugInfo/DWARF/DWARFExpression.h"19#include <optional>2021using namespace lldb;22using namespace lldb_private;2324bool UnwindPlan::Row::RegisterLocation::25operator==(const UnwindPlan::Row::RegisterLocation &rhs) const {26if (m_type == rhs.m_type) {27switch (m_type) {28case unspecified:29case undefined:30case same:31return true;3233case atCFAPlusOffset:34case isCFAPlusOffset:35case atAFAPlusOffset:36case isAFAPlusOffset:37return m_location.offset == rhs.m_location.offset;3839case inOtherRegister:40return m_location.reg_num == rhs.m_location.reg_num;4142case atDWARFExpression:43case isDWARFExpression:44if (m_location.expr.length == rhs.m_location.expr.length)45return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes,46m_location.expr.length);47break;48}49}50return false;51}5253// This function doesn't copy the dwarf expression bytes; they must remain in54// allocated memory for the lifespan of this UnwindPlan object.55void UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression(56const uint8_t *opcodes, uint32_t len) {57m_type = atDWARFExpression;58m_location.expr.opcodes = opcodes;59m_location.expr.length = len;60}6162// This function doesn't copy the dwarf expression bytes; they must remain in63// allocated memory for the lifespan of this UnwindPlan object.64void UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression(65const uint8_t *opcodes, uint32_t len) {66m_type = isDWARFExpression;67m_location.expr.opcodes = opcodes;68m_location.expr.length = len;69}7071static std::optional<std::pair<lldb::ByteOrder, uint32_t>>72GetByteOrderAndAddrSize(Thread *thread) {73if (!thread)74return std::nullopt;75ProcessSP process_sp = thread->GetProcess();76if (!process_sp)77return std::nullopt;78ArchSpec arch = process_sp->GetTarget().GetArchitecture();79return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());80}8182static void DumpDWARFExpr(Stream &s, llvm::ArrayRef<uint8_t> expr, Thread *thread) {83if (auto order_and_width = GetByteOrderAndAddrSize(thread)) {84llvm::DataExtractor data(expr, order_and_width->first == eByteOrderLittle,85order_and_width->second);86llvm::DWARFExpression(data, order_and_width->second, llvm::dwarf::DWARF32)87.print(s.AsRawOstream(), llvm::DIDumpOptions(), nullptr);88} else89s.PutCString("dwarf-expr");90}9192void UnwindPlan::Row::RegisterLocation::Dump(Stream &s,93const UnwindPlan *unwind_plan,94const UnwindPlan::Row *row,95Thread *thread,96bool verbose) const {97switch (m_type) {98case unspecified:99if (verbose)100s.PutCString("=<unspec>");101else102s.PutCString("=!");103break;104case undefined:105if (verbose)106s.PutCString("=<undef>");107else108s.PutCString("=?");109break;110case same:111s.PutCString("= <same>");112break;113114case atCFAPlusOffset:115case isCFAPlusOffset: {116s.PutChar('=');117if (m_type == atCFAPlusOffset)118s.PutChar('[');119s.Printf("CFA%+d", m_location.offset);120if (m_type == atCFAPlusOffset)121s.PutChar(']');122} break;123124case atAFAPlusOffset:125case isAFAPlusOffset: {126s.PutChar('=');127if (m_type == atAFAPlusOffset)128s.PutChar('[');129s.Printf("AFA%+d", m_location.offset);130if (m_type == atAFAPlusOffset)131s.PutChar(']');132} break;133134case inOtherRegister: {135const RegisterInfo *other_reg_info = nullptr;136if (unwind_plan)137other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num);138if (other_reg_info)139s.Printf("=%s", other_reg_info->name);140else141s.Printf("=reg(%u)", m_location.reg_num);142} break;143144case atDWARFExpression:145case isDWARFExpression: {146s.PutChar('=');147if (m_type == atDWARFExpression)148s.PutChar('[');149DumpDWARFExpr(150s, llvm::ArrayRef(m_location.expr.opcodes, m_location.expr.length),151thread);152if (m_type == atDWARFExpression)153s.PutChar(']');154} break;155}156}157158static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan,159Thread *thread, uint32_t reg_num) {160const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num);161if (reg_info)162s.PutCString(reg_info->name);163else164s.Printf("reg(%u)", reg_num);165}166167bool UnwindPlan::Row::FAValue::168operator==(const UnwindPlan::Row::FAValue &rhs) const {169if (m_type == rhs.m_type) {170switch (m_type) {171case unspecified:172case isRaSearch:173return m_value.ra_search_offset == rhs.m_value.ra_search_offset;174175case isRegisterPlusOffset:176return m_value.reg.offset == rhs.m_value.reg.offset;177178case isRegisterDereferenced:179return m_value.reg.reg_num == rhs.m_value.reg.reg_num;180181case isDWARFExpression:182if (m_value.expr.length == rhs.m_value.expr.length)183return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes,184m_value.expr.length);185break;186}187}188return false;189}190191void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan,192Thread *thread) const {193switch (m_type) {194case isRegisterPlusOffset:195DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);196s.Printf("%+3d", m_value.reg.offset);197break;198case isRegisterDereferenced:199s.PutChar('[');200DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);201s.PutChar(']');202break;203case isDWARFExpression:204DumpDWARFExpr(s, llvm::ArrayRef(m_value.expr.opcodes, m_value.expr.length),205thread);206break;207case unspecified:208s.PutCString("unspecified");209break;210case isRaSearch:211s.Printf("RaSearch@SP%+d", m_value.ra_search_offset);212break;213}214}215216void UnwindPlan::Row::Clear() {217m_cfa_value.SetUnspecified();218m_afa_value.SetUnspecified();219m_offset = 0;220m_unspecified_registers_are_undefined = false;221m_register_locations.clear();222}223224void UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan,225Thread *thread, addr_t base_addr) const {226if (base_addr != LLDB_INVALID_ADDRESS)227s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset());228else229s.Printf("%4" PRId64 ": CFA=", GetOffset());230231m_cfa_value.Dump(s, unwind_plan, thread);232233if (!m_afa_value.IsUnspecified()) {234s.Printf(" AFA=");235m_afa_value.Dump(s, unwind_plan, thread);236}237238s.Printf(" => ");239for (collection::const_iterator idx = m_register_locations.begin();240idx != m_register_locations.end(); ++idx) {241DumpRegisterName(s, unwind_plan, thread, idx->first);242const bool verbose = false;243idx->second.Dump(s, unwind_plan, this, thread, verbose);244s.PutChar(' ');245}246}247248UnwindPlan::Row::Row() : m_cfa_value(), m_afa_value(), m_register_locations() {}249250bool UnwindPlan::Row::GetRegisterInfo(251uint32_t reg_num,252UnwindPlan::Row::RegisterLocation ®ister_location) const {253collection::const_iterator pos = m_register_locations.find(reg_num);254if (pos != m_register_locations.end()) {255register_location = pos->second;256return true;257}258if (m_unspecified_registers_are_undefined) {259register_location.SetUndefined();260return true;261}262return false;263}264265void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) {266collection::const_iterator pos = m_register_locations.find(reg_num);267if (pos != m_register_locations.end()) {268m_register_locations.erase(pos);269}270}271272void UnwindPlan::Row::SetRegisterInfo(273uint32_t reg_num,274const UnwindPlan::Row::RegisterLocation register_location) {275m_register_locations[reg_num] = register_location;276}277278bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,279int32_t offset,280bool can_replace) {281if (!can_replace &&282m_register_locations.find(reg_num) != m_register_locations.end())283return false;284RegisterLocation reg_loc;285reg_loc.SetAtCFAPlusOffset(offset);286m_register_locations[reg_num] = reg_loc;287return true;288}289290bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,291int32_t offset,292bool can_replace) {293if (!can_replace &&294m_register_locations.find(reg_num) != m_register_locations.end())295return false;296RegisterLocation reg_loc;297reg_loc.SetIsCFAPlusOffset(offset);298m_register_locations[reg_num] = reg_loc;299return true;300}301302bool UnwindPlan::Row::SetRegisterLocationToUndefined(303uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) {304collection::iterator pos = m_register_locations.find(reg_num);305collection::iterator end = m_register_locations.end();306307if (pos != end) {308if (!can_replace)309return false;310if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())311return false;312}313RegisterLocation reg_loc;314reg_loc.SetUndefined();315m_register_locations[reg_num] = reg_loc;316return true;317}318319bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num,320bool can_replace) {321if (!can_replace &&322m_register_locations.find(reg_num) != m_register_locations.end())323return false;324RegisterLocation reg_loc;325reg_loc.SetUnspecified();326m_register_locations[reg_num] = reg_loc;327return true;328}329330bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num,331uint32_t other_reg_num,332bool can_replace) {333if (!can_replace &&334m_register_locations.find(reg_num) != m_register_locations.end())335return false;336RegisterLocation reg_loc;337reg_loc.SetInRegister(other_reg_num);338m_register_locations[reg_num] = reg_loc;339return true;340}341342bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,343bool must_replace) {344if (must_replace &&345m_register_locations.find(reg_num) == m_register_locations.end())346return false;347RegisterLocation reg_loc;348reg_loc.SetSame();349m_register_locations[reg_num] = reg_loc;350return true;351}352353bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const {354return m_offset == rhs.m_offset && m_cfa_value == rhs.m_cfa_value &&355m_afa_value == rhs.m_afa_value &&356m_unspecified_registers_are_undefined ==357rhs.m_unspecified_registers_are_undefined &&358m_register_locations == rhs.m_register_locations;359}360361void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) {362if (m_row_list.empty() ||363m_row_list.back()->GetOffset() != row_sp->GetOffset())364m_row_list.push_back(row_sp);365else366m_row_list.back() = row_sp;367}368369void UnwindPlan::InsertRow(const UnwindPlan::RowSP &row_sp,370bool replace_existing) {371collection::iterator it = m_row_list.begin();372while (it != m_row_list.end()) {373RowSP row = *it;374if (row->GetOffset() >= row_sp->GetOffset())375break;376it++;377}378if (it == m_row_list.end() || (*it)->GetOffset() != row_sp->GetOffset())379m_row_list.insert(it, row_sp);380else if (replace_existing)381*it = row_sp;382}383384UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const {385RowSP row;386if (!m_row_list.empty()) {387if (offset == -1)388row = m_row_list.back();389else {390collection::const_iterator pos, end = m_row_list.end();391for (pos = m_row_list.begin(); pos != end; ++pos) {392if ((*pos)->GetOffset() <= static_cast<lldb::offset_t>(offset))393row = *pos;394else395break;396}397}398}399return row;400}401402bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {403return idx < m_row_list.size();404}405406const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const {407if (idx < m_row_list.size())408return m_row_list[idx];409else {410Log *log = GetLog(LLDBLog::Unwind);411LLDB_LOGF(log,412"error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index "413"(number rows is %u)",414idx, (uint32_t)m_row_list.size());415return UnwindPlan::RowSP();416}417}418419const UnwindPlan::RowSP UnwindPlan::GetLastRow() const {420if (m_row_list.empty()) {421Log *log = GetLog(LLDBLog::Unwind);422LLDB_LOGF(log, "UnwindPlan::GetLastRow() when rows are empty");423return UnwindPlan::RowSP();424}425return m_row_list.back();426}427428int UnwindPlan::GetRowCount() const { return m_row_list.size(); }429430void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) {431if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)432m_plan_valid_address_range = range;433}434435bool UnwindPlan::PlanValidAtAddress(Address addr) {436// If this UnwindPlan has no rows, it is an invalid UnwindPlan.437if (GetRowCount() == 0) {438Log *log = GetLog(LLDBLog::Unwind);439if (log) {440StreamString s;441if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {442LLDB_LOGF(log,443"UnwindPlan is invalid -- no unwind rows for UnwindPlan "444"'%s' at address %s",445m_source_name.GetCString(), s.GetData());446} else {447LLDB_LOGF(log,448"UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",449m_source_name.GetCString());450}451}452return false;453}454455// If the 0th Row of unwind instructions is missing, or if it doesn't provide456// a register to use to find the Canonical Frame Address, this is not a valid457// UnwindPlan.458if (GetRowAtIndex(0).get() == nullptr ||459GetRowAtIndex(0)->GetCFAValue().GetValueType() ==460Row::FAValue::unspecified) {461Log *log = GetLog(LLDBLog::Unwind);462if (log) {463StreamString s;464if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {465LLDB_LOGF(log,466"UnwindPlan is invalid -- no CFA register defined in row 0 "467"for UnwindPlan '%s' at address %s",468m_source_name.GetCString(), s.GetData());469} else {470LLDB_LOGF(log,471"UnwindPlan is invalid -- no CFA register defined in row 0 "472"for UnwindPlan '%s'",473m_source_name.GetCString());474}475}476return false;477}478479if (!m_plan_valid_address_range.GetBaseAddress().IsValid() ||480m_plan_valid_address_range.GetByteSize() == 0)481return true;482483if (!addr.IsValid())484return true;485486if (m_plan_valid_address_range.ContainsFileAddress(addr))487return true;488489return false;490}491492void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {493if (!m_source_name.IsEmpty()) {494s.Printf("This UnwindPlan originally sourced from %s\n",495m_source_name.GetCString());496}497if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) {498TargetSP target_sp(thread->CalculateTarget());499addr_t lsda_load_addr = m_lsda_address.GetLoadAddress(target_sp.get());500addr_t personality_func_load_addr =501m_personality_func_addr.GetLoadAddress(target_sp.get());502503if (lsda_load_addr != LLDB_INVALID_ADDRESS &&504personality_func_load_addr != LLDB_INVALID_ADDRESS) {505s.Printf("LSDA address 0x%" PRIx64506", personality routine is at address 0x%" PRIx64 "\n",507lsda_load_addr, personality_func_load_addr);508}509}510s.Printf("This UnwindPlan is sourced from the compiler: ");511switch (m_plan_is_sourced_from_compiler) {512case eLazyBoolYes:513s.Printf("yes.\n");514break;515case eLazyBoolNo:516s.Printf("no.\n");517break;518case eLazyBoolCalculate:519s.Printf("not specified.\n");520break;521}522s.Printf("This UnwindPlan is valid at all instruction locations: ");523switch (m_plan_is_valid_at_all_instruction_locations) {524case eLazyBoolYes:525s.Printf("yes.\n");526break;527case eLazyBoolNo:528s.Printf("no.\n");529break;530case eLazyBoolCalculate:531s.Printf("not specified.\n");532break;533}534s.Printf("This UnwindPlan is for a trap handler function: ");535switch (m_plan_is_for_signal_trap) {536case eLazyBoolYes:537s.Printf("yes.\n");538break;539case eLazyBoolNo:540s.Printf("no.\n");541break;542case eLazyBoolCalculate:543s.Printf("not specified.\n");544break;545}546if (m_plan_valid_address_range.GetBaseAddress().IsValid() &&547m_plan_valid_address_range.GetByteSize() > 0) {548s.PutCString("Address range of this UnwindPlan: ");549TargetSP target_sp(thread->CalculateTarget());550m_plan_valid_address_range.Dump(&s, target_sp.get(),551Address::DumpStyleSectionNameOffset);552s.EOL();553}554collection::const_iterator pos, begin = m_row_list.begin(),555end = m_row_list.end();556for (pos = begin; pos != end; ++pos) {557s.Printf("row[%u]: ", (uint32_t)std::distance(begin, pos));558(*pos)->Dump(s, this, thread, base_addr);559s.Printf("\n");560}561}562563void UnwindPlan::SetSourceName(const char *source) {564m_source_name = ConstString(source);565}566567ConstString UnwindPlan::GetSourceName() const { return m_source_name; }568569const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,570uint32_t unwind_reg) const {571if (thread) {572RegisterContext *reg_ctx = thread->GetRegisterContext().get();573if (reg_ctx) {574uint32_t reg;575if (m_register_kind == eRegisterKindLLDB)576reg = unwind_reg;577else578reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,579unwind_reg);580if (reg != LLDB_INVALID_REGNUM)581return reg_ctx->GetRegisterInfoAtIndex(reg);582}583}584return nullptr;585}586587588