589 lines · cpp
1//===-- UnwindPlan.cpp ----------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Symbol/UnwindPlan.h"10 11#include "lldb/Target/Process.h"12#include "lldb/Target/RegisterContext.h"13#include "lldb/Target/Target.h"14#include "lldb/Target/Thread.h"15#include "lldb/Utility/ConstString.h"16#include "lldb/Utility/LLDBLog.h"17#include "lldb/Utility/Log.h"18#include "llvm/ADT/STLExtras.h"19#include "llvm/DebugInfo/DIContext.h"20#include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h"21#include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"22#include <optional>23 24using namespace lldb;25using namespace lldb_private;26 27bool UnwindPlan::Row::AbstractRegisterLocation::operator==(28 const UnwindPlan::Row::AbstractRegisterLocation &rhs) const {29 if (m_type == rhs.m_type) {30 switch (m_type) {31 case unspecified:32 case undefined:33 case same:34 return true;35 36 case atCFAPlusOffset:37 case isCFAPlusOffset:38 case atAFAPlusOffset:39 case isAFAPlusOffset:40 return m_location.offset == rhs.m_location.offset;41 42 case inOtherRegister:43 return m_location.reg_num == rhs.m_location.reg_num;44 45 case atDWARFExpression:46 case isDWARFExpression:47 if (m_location.expr.length == rhs.m_location.expr.length)48 return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes,49 m_location.expr.length);50 break;51 case isConstant:52 return m_location.constant_value == rhs.m_location.constant_value;53 }54 }55 return false;56}57 58// This function doesn't copy the dwarf expression bytes; they must remain in59// allocated memory for the lifespan of this UnwindPlan object.60void UnwindPlan::Row::AbstractRegisterLocation::SetAtDWARFExpression(61 const uint8_t *opcodes, uint32_t len) {62 m_type = atDWARFExpression;63 m_location.expr.opcodes = opcodes;64 m_location.expr.length = len;65}66 67// This function doesn't copy the dwarf expression bytes; they must remain in68// allocated memory for the lifespan of this UnwindPlan object.69void UnwindPlan::Row::AbstractRegisterLocation::SetIsDWARFExpression(70 const uint8_t *opcodes, uint32_t len) {71 m_type = isDWARFExpression;72 m_location.expr.opcodes = opcodes;73 m_location.expr.length = len;74}75 76static std::optional<std::pair<lldb::ByteOrder, uint32_t>>77GetByteOrderAndAddrSize(Thread *thread) {78 if (!thread)79 return std::nullopt;80 ProcessSP process_sp = thread->GetProcess();81 if (!process_sp)82 return std::nullopt;83 ArchSpec arch = process_sp->GetTarget().GetArchitecture();84 return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());85}86 87static void DumpDWARFExpr(Stream &s, llvm::ArrayRef<uint8_t> expr, Thread *thread) {88 if (auto order_and_width = GetByteOrderAndAddrSize(thread)) {89 llvm::DataExtractor data(expr, order_and_width->first == eByteOrderLittle,90 order_and_width->second);91 llvm::DWARFExpression E(data, order_and_width->second,92 llvm::dwarf::DWARF32);93 printDwarfExpression(&E, s.AsRawOstream(), llvm::DIDumpOptions(), nullptr);94 } else95 s.PutCString("dwarf-expr");96}97 98void UnwindPlan::Row::AbstractRegisterLocation::Dump(99 Stream &s, const UnwindPlan *unwind_plan, const UnwindPlan::Row *row,100 Thread *thread, bool verbose) const {101 switch (m_type) {102 case unspecified:103 if (verbose)104 s.PutCString("=<unspec>");105 else106 s.PutCString("=!");107 break;108 case undefined:109 if (verbose)110 s.PutCString("=<undef>");111 else112 s.PutCString("=?");113 break;114 case same:115 s.PutCString("= <same>");116 break;117 118 case atCFAPlusOffset:119 case isCFAPlusOffset: {120 s.PutChar('=');121 if (m_type == atCFAPlusOffset)122 s.PutChar('[');123 s.Printf("CFA%+d", m_location.offset);124 if (m_type == atCFAPlusOffset)125 s.PutChar(']');126 } break;127 128 case atAFAPlusOffset:129 case isAFAPlusOffset: {130 s.PutChar('=');131 if (m_type == atAFAPlusOffset)132 s.PutChar('[');133 s.Printf("AFA%+d", m_location.offset);134 if (m_type == atAFAPlusOffset)135 s.PutChar(']');136 } break;137 138 case inOtherRegister: {139 const RegisterInfo *other_reg_info = nullptr;140 if (unwind_plan)141 other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num);142 if (other_reg_info)143 s.Printf("=%s", other_reg_info->name);144 else145 s.Printf("=reg(%u)", m_location.reg_num);146 } break;147 148 case atDWARFExpression:149 case isDWARFExpression: {150 s.PutChar('=');151 if (m_type == atDWARFExpression)152 s.PutChar('[');153 DumpDWARFExpr(154 s, llvm::ArrayRef(m_location.expr.opcodes, m_location.expr.length),155 thread);156 if (m_type == atDWARFExpression)157 s.PutChar(']');158 } break;159 case isConstant:160 s.Printf("=0x%" PRIx64, m_location.constant_value);161 break;162 }163}164 165static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan,166 Thread *thread, uint32_t reg_num) {167 const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num);168 if (reg_info)169 s.PutCString(reg_info->name);170 else171 s.Printf("reg(%u)", reg_num);172}173 174bool UnwindPlan::Row::FAValue::175operator==(const UnwindPlan::Row::FAValue &rhs) const {176 if (m_type == rhs.m_type) {177 switch (m_type) {178 case unspecified:179 case isRaSearch:180 return m_value.ra_search_offset == rhs.m_value.ra_search_offset;181 182 case isRegisterPlusOffset:183 return m_value.reg.offset == rhs.m_value.reg.offset;184 185 case isRegisterDereferenced:186 return m_value.reg.reg_num == rhs.m_value.reg.reg_num;187 188 case isDWARFExpression:189 if (m_value.expr.length == rhs.m_value.expr.length)190 return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes,191 m_value.expr.length);192 break;193 case isConstant:194 return m_value.constant == rhs.m_value.constant;195 }196 }197 return false;198}199 200void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan,201 Thread *thread) const {202 switch (m_type) {203 case isRegisterPlusOffset:204 DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);205 s.Printf("%+3d", m_value.reg.offset);206 break;207 case isRegisterDereferenced:208 s.PutChar('[');209 DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);210 s.PutChar(']');211 break;212 case isDWARFExpression:213 DumpDWARFExpr(s, llvm::ArrayRef(m_value.expr.opcodes, m_value.expr.length),214 thread);215 break;216 case unspecified:217 s.PutCString("unspecified");218 break;219 case isRaSearch:220 s.Printf("RaSearch@SP%+d", m_value.ra_search_offset);221 break;222 case isConstant:223 s.Printf("0x%" PRIx64, m_value.constant);224 }225}226 227void UnwindPlan::Row::Clear() {228 m_cfa_value.SetUnspecified();229 m_afa_value.SetUnspecified();230 m_offset = 0;231 m_unspecified_registers_are_undefined = false;232 m_register_locations.clear();233}234 235void UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan,236 Thread *thread, addr_t base_addr) const {237 if (base_addr != LLDB_INVALID_ADDRESS)238 s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset());239 else240 s.Printf("%4" PRId64 ": CFA=", GetOffset());241 242 m_cfa_value.Dump(s, unwind_plan, thread);243 244 if (!m_afa_value.IsUnspecified()) {245 s.Printf(" AFA=");246 m_afa_value.Dump(s, unwind_plan, thread);247 }248 249 s.Printf(" => ");250 for (collection::const_iterator idx = m_register_locations.begin();251 idx != m_register_locations.end(); ++idx) {252 DumpRegisterName(s, unwind_plan, thread, idx->first);253 const bool verbose = false;254 idx->second.Dump(s, unwind_plan, this, thread, verbose);255 s.PutChar(' ');256 }257}258 259UnwindPlan::Row::Row() : m_cfa_value(), m_afa_value(), m_register_locations() {}260 261bool UnwindPlan::Row::GetRegisterInfo(262 uint32_t reg_num,263 UnwindPlan::Row::AbstractRegisterLocation ®ister_location) const {264 collection::const_iterator pos = m_register_locations.find(reg_num);265 if (pos != m_register_locations.end()) {266 register_location = pos->second;267 return true;268 }269 if (m_unspecified_registers_are_undefined) {270 register_location.SetUndefined();271 return true;272 }273 return false;274}275 276void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) {277 collection::const_iterator pos = m_register_locations.find(reg_num);278 if (pos != m_register_locations.end()) {279 m_register_locations.erase(pos);280 }281}282 283void UnwindPlan::Row::SetRegisterInfo(284 uint32_t reg_num,285 const UnwindPlan::Row::AbstractRegisterLocation register_location) {286 m_register_locations[reg_num] = register_location;287}288 289bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,290 int32_t offset,291 bool can_replace) {292 if (!can_replace &&293 m_register_locations.find(reg_num) != m_register_locations.end())294 return false;295 AbstractRegisterLocation reg_loc;296 reg_loc.SetAtCFAPlusOffset(offset);297 m_register_locations[reg_num] = reg_loc;298 return true;299}300 301bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,302 int32_t offset,303 bool can_replace) {304 if (!can_replace &&305 m_register_locations.find(reg_num) != m_register_locations.end())306 return false;307 AbstractRegisterLocation reg_loc;308 reg_loc.SetIsCFAPlusOffset(offset);309 m_register_locations[reg_num] = reg_loc;310 return true;311}312 313bool UnwindPlan::Row::SetRegisterLocationToUndefined(314 uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) {315 collection::iterator pos = m_register_locations.find(reg_num);316 collection::iterator end = m_register_locations.end();317 318 if (pos != end) {319 if (!can_replace)320 return false;321 if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())322 return false;323 }324 AbstractRegisterLocation reg_loc;325 reg_loc.SetUndefined();326 m_register_locations[reg_num] = reg_loc;327 return true;328}329 330bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num,331 bool can_replace) {332 if (!can_replace &&333 m_register_locations.find(reg_num) != m_register_locations.end())334 return false;335 AbstractRegisterLocation reg_loc;336 reg_loc.SetUnspecified();337 m_register_locations[reg_num] = reg_loc;338 return true;339}340 341bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num,342 uint32_t other_reg_num,343 bool can_replace) {344 if (!can_replace &&345 m_register_locations.find(reg_num) != m_register_locations.end())346 return false;347 AbstractRegisterLocation reg_loc;348 reg_loc.SetInRegister(other_reg_num);349 m_register_locations[reg_num] = reg_loc;350 return true;351}352 353bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,354 bool must_replace) {355 if (must_replace &&356 m_register_locations.find(reg_num) == m_register_locations.end())357 return false;358 AbstractRegisterLocation reg_loc;359 reg_loc.SetSame();360 m_register_locations[reg_num] = reg_loc;361 return true;362}363 364bool UnwindPlan::Row::SetRegisterLocationToIsDWARFExpression(365 uint32_t reg_num, const uint8_t *opcodes, uint32_t len, bool can_replace) {366 if (!can_replace &&367 m_register_locations.find(reg_num) != m_register_locations.end())368 return false;369 AbstractRegisterLocation reg_loc;370 reg_loc.SetIsDWARFExpression(opcodes, len);371 m_register_locations[reg_num] = reg_loc;372 return true;373}374 375bool UnwindPlan::Row::SetRegisterLocationToIsConstant(uint32_t reg_num,376 uint64_t constant,377 bool can_replace) {378 if (!can_replace &&379 m_register_locations.find(reg_num) != m_register_locations.end())380 return false;381 AbstractRegisterLocation reg_loc;382 reg_loc.SetIsConstant(constant);383 m_register_locations[reg_num] = reg_loc;384 return true;385}386 387bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const {388 return m_offset == rhs.m_offset && m_cfa_value == rhs.m_cfa_value &&389 m_afa_value == rhs.m_afa_value &&390 m_unspecified_registers_are_undefined ==391 rhs.m_unspecified_registers_are_undefined &&392 m_register_locations == rhs.m_register_locations;393}394 395void UnwindPlan::AppendRow(Row row) {396 if (m_row_list.empty() || m_row_list.back().GetOffset() != row.GetOffset())397 m_row_list.push_back(std::move(row));398 else399 m_row_list.back() = std::move(row);400}401 402struct RowLess {403 bool operator()(int64_t a, const UnwindPlan::Row &b) const {404 return a < b.GetOffset();405 }406 bool operator()(const UnwindPlan::Row &a, int64_t b) const {407 return a.GetOffset() < b;408 }409};410 411void UnwindPlan::InsertRow(Row row, bool replace_existing) {412 auto it = llvm::lower_bound(m_row_list, row.GetOffset(), RowLess());413 if (it == m_row_list.end() || it->GetOffset() > row.GetOffset())414 m_row_list.insert(it, std::move(row));415 else {416 assert(it->GetOffset() == row.GetOffset());417 if (replace_existing)418 *it = std::move(row);419 }420}421 422const UnwindPlan::Row *423UnwindPlan::GetRowForFunctionOffset(std::optional<int64_t> offset) const {424 auto it = offset ? llvm::upper_bound(m_row_list, *offset, RowLess())425 : m_row_list.end();426 if (it == m_row_list.begin())427 return nullptr;428 // upper_bound returns the row strictly greater than our desired offset, which429 // means that the row before it is a match.430 return &*std::prev(it);431}432 433bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {434 return idx < m_row_list.size();435}436 437const UnwindPlan::Row *UnwindPlan::GetRowAtIndex(uint32_t idx) const {438 if (idx < m_row_list.size())439 return &m_row_list[idx];440 LLDB_LOG(GetLog(LLDBLog::Unwind),441 "error: UnwindPlan::GetRowAtIndex(idx = {0}) invalid index "442 "(number rows is {1})",443 idx, m_row_list.size());444 return nullptr;445}446 447const UnwindPlan::Row *UnwindPlan::GetLastRow() const {448 if (m_row_list.empty()) {449 LLDB_LOG(GetLog(LLDBLog::Unwind),450 "UnwindPlan::GetLastRow() when rows are empty");451 return nullptr;452 }453 return &m_row_list.back();454}455 456bool UnwindPlan::PlanValidAtAddress(Address addr) const {457 // If this UnwindPlan has no rows, it is an invalid UnwindPlan.458 if (GetRowCount() == 0) {459 Log *log = GetLog(LLDBLog::Unwind);460 if (log) {461 StreamString s;462 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {463 LLDB_LOGF(log,464 "UnwindPlan is invalid -- no unwind rows for UnwindPlan "465 "'%s' at address %s",466 m_source_name.GetCString(), s.GetData());467 } else {468 LLDB_LOGF(log,469 "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",470 m_source_name.GetCString());471 }472 }473 return false;474 }475 476 // If the 0th Row of unwind instructions is missing, or if it doesn't provide477 // a register to use to find the Canonical Frame Address, this is not a valid478 // UnwindPlan.479 const Row *row0 = GetRowAtIndex(0);480 if (!row0 ||481 row0->GetCFAValue().GetValueType() == Row::FAValue::unspecified) {482 Log *log = GetLog(LLDBLog::Unwind);483 if (log) {484 StreamString s;485 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {486 LLDB_LOGF(log,487 "UnwindPlan is invalid -- no CFA register defined in row 0 "488 "for UnwindPlan '%s' at address %s",489 m_source_name.GetCString(), s.GetData());490 } else {491 LLDB_LOGF(log,492 "UnwindPlan is invalid -- no CFA register defined in row 0 "493 "for UnwindPlan '%s'",494 m_source_name.GetCString());495 }496 }497 return false;498 }499 500 if (m_plan_valid_ranges.empty())501 return true;502 503 if (!addr.IsValid())504 return true;505 506 return llvm::any_of(m_plan_valid_ranges, [&](const AddressRange &range) {507 return range.ContainsFileAddress(addr);508 });509}510 511void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {512 if (!m_source_name.IsEmpty()) {513 s.Printf("This UnwindPlan originally sourced from %s\n",514 m_source_name.GetCString());515 }516 s.Printf("This UnwindPlan is sourced from the compiler: ");517 switch (m_plan_is_sourced_from_compiler) {518 case eLazyBoolYes:519 s.Printf("yes.\n");520 break;521 case eLazyBoolNo:522 s.Printf("no.\n");523 break;524 case eLazyBoolCalculate:525 s.Printf("not specified.\n");526 break;527 }528 s.Printf("This UnwindPlan is valid at all instruction locations: ");529 switch (m_plan_is_valid_at_all_instruction_locations) {530 case eLazyBoolYes:531 s.Printf("yes.\n");532 break;533 case eLazyBoolNo:534 s.Printf("no.\n");535 break;536 case eLazyBoolCalculate:537 s.Printf("not specified.\n");538 break;539 }540 s.Printf("This UnwindPlan is for a trap handler function: ");541 switch (m_plan_is_for_signal_trap) {542 case eLazyBoolYes:543 s.Printf("yes.\n");544 break;545 case eLazyBoolNo:546 s.Printf("no.\n");547 break;548 case eLazyBoolCalculate:549 s.Printf("not specified.\n");550 break;551 }552 if (!m_plan_valid_ranges.empty()) {553 s.PutCString("Address range of this UnwindPlan: ");554 TargetSP target_sp(thread->CalculateTarget());555 for (const AddressRange &range : m_plan_valid_ranges)556 range.Dump(&s, target_sp.get(), Address::DumpStyleSectionNameOffset);557 s.EOL();558 }559 for (const auto &[index, row] : llvm::enumerate(m_row_list)) {560 s.Format("row[{0}]: ", index);561 row.Dump(s, this, thread, base_addr);562 s << "\n";563 }564}565 566void UnwindPlan::SetSourceName(const char *source) {567 m_source_name = ConstString(source);568}569 570ConstString UnwindPlan::GetSourceName() const { return m_source_name; }571 572const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,573 uint32_t unwind_reg) const {574 if (thread) {575 RegisterContext *reg_ctx = thread->GetRegisterContext().get();576 if (reg_ctx) {577 uint32_t reg;578 if (m_register_kind == eRegisterKindLLDB)579 reg = unwind_reg;580 else581 reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,582 unwind_reg);583 if (reg != LLDB_INVALID_REGNUM)584 return reg_ctx->GetRegisterInfoAtIndex(reg);585 }586 }587 return nullptr;588}589