1 //===-- UnwindPlan.cpp ----------------------------------*- C++ -*-===//
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-exception
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Symbol/UnwindPlan.h"
11 #include "lldb/Expression/DWARFExpression.h"
12 #include "lldb/Target/Process.h"
13 #include "lldb/Target/RegisterContext.h"
14 #include "lldb/Target/Target.h"
15 #include "lldb/Target/Thread.h"
16 #include "lldb/Utility/ConstString.h"
17 #include "lldb/Utility/Log.h"
20 using namespace lldb_private;
22 bool UnwindPlan::Row::RegisterLocation::
23 operator==(const UnwindPlan::Row::RegisterLocation &rhs) const {
24 if (m_type == rhs.m_type) {
35 return m_location.offset == rhs.m_location.offset;
38 return m_location.reg_num == rhs.m_location.reg_num;
40 case atDWARFExpression:
41 case isDWARFExpression:
42 if (m_location.expr.length == rhs.m_location.expr.length)
43 return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes,
44 m_location.expr.length);
51 // This function doesn't copy the dwarf expression bytes; they must remain in
52 // allocated memory for the lifespan of this UnwindPlan object.
53 void UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression(
54 const uint8_t *opcodes, uint32_t len) {
55 m_type = atDWARFExpression;
56 m_location.expr.opcodes = opcodes;
57 m_location.expr.length = len;
60 // This function doesn't copy the dwarf expression bytes; they must remain in
61 // allocated memory for the lifespan of this UnwindPlan object.
62 void UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression(
63 const uint8_t *opcodes, uint32_t len) {
64 m_type = isDWARFExpression;
65 m_location.expr.opcodes = opcodes;
66 m_location.expr.length = len;
69 static llvm::Optional<std::pair<lldb::ByteOrder, uint32_t>>
70 GetByteOrderAndAddrSize(Thread *thread) {
73 ProcessSP process_sp = thread->GetProcess();
76 ArchSpec arch = process_sp->GetTarget().GetArchitecture();
77 return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());
80 static void DumpDWARFExpr(Stream &s, llvm::ArrayRef<uint8_t> expr, Thread *thread) {
81 if (auto order_and_width = GetByteOrderAndAddrSize(thread)) {
82 DataExtractor extractor(expr.data(), expr.size(), order_and_width->first,
83 order_and_width->second);
84 if (!DWARFExpression::PrintDWARFExpression(s, extractor,
85 order_and_width->second,
87 /*location_expression*/ false))
88 s.PutCString("invalid-dwarf-expr");
90 s.PutCString("dwarf-expr");
93 void UnwindPlan::Row::RegisterLocation::Dump(Stream &s,
94 const UnwindPlan *unwind_plan,
95 const UnwindPlan::Row *row,
101 s.PutCString("=<unspec>");
107 s.PutCString("=<undef>");
112 s.PutCString("= <same>");
115 case atCFAPlusOffset:
116 case isCFAPlusOffset: {
118 if (m_type == atCFAPlusOffset)
120 s.Printf("CFA%+d", m_location.offset);
121 if (m_type == atCFAPlusOffset)
125 case atAFAPlusOffset:
126 case isAFAPlusOffset: {
128 if (m_type == atAFAPlusOffset)
130 s.Printf("AFA%+d", m_location.offset);
131 if (m_type == atAFAPlusOffset)
135 case inOtherRegister: {
136 const RegisterInfo *other_reg_info = nullptr;
138 other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num);
140 s.Printf("=%s", other_reg_info->name);
142 s.Printf("=reg(%u)", m_location.reg_num);
145 case atDWARFExpression:
146 case isDWARFExpression: {
148 if (m_type == atDWARFExpression)
151 s, llvm::makeArrayRef(m_location.expr.opcodes, m_location.expr.length),
153 if (m_type == atDWARFExpression)
159 static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan,
160 Thread *thread, uint32_t reg_num) {
161 const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num);
163 s.PutCString(reg_info->name);
165 s.Printf("reg(%u)", reg_num);
168 bool UnwindPlan::Row::FAValue::
169 operator==(const UnwindPlan::Row::FAValue &rhs) const {
170 if (m_type == rhs.m_type) {
175 case isRegisterPlusOffset:
176 return m_value.reg.offset == rhs.m_value.reg.offset;
178 case isRegisterDereferenced:
179 return m_value.reg.reg_num == rhs.m_value.reg.reg_num;
181 case isDWARFExpression:
182 if (m_value.expr.length == rhs.m_value.expr.length)
183 return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes,
184 m_value.expr.length);
191 void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan,
192 Thread *thread) const {
194 case isRegisterPlusOffset:
195 DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
196 s.Printf("%+3d", m_value.reg.offset);
198 case isRegisterDereferenced:
200 DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
203 case isDWARFExpression:
205 llvm::makeArrayRef(m_value.expr.opcodes, m_value.expr.length),
209 s.PutCString("unspecified");
214 void UnwindPlan::Row::Clear() {
215 m_cfa_value.SetUnspecified();
216 m_afa_value.SetUnspecified();
218 m_register_locations.clear();
221 void UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan,
222 Thread *thread, addr_t base_addr) const {
223 if (base_addr != LLDB_INVALID_ADDRESS)
224 s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset());
226 s.Printf("%4" PRId64 ": CFA=", GetOffset());
228 m_cfa_value.Dump(s, unwind_plan, thread);
230 if (!m_afa_value.IsUnspecified()) {
232 m_afa_value.Dump(s, unwind_plan, thread);
236 for (collection::const_iterator idx = m_register_locations.begin();
237 idx != m_register_locations.end(); ++idx) {
238 DumpRegisterName(s, unwind_plan, thread, idx->first);
239 const bool verbose = false;
240 idx->second.Dump(s, unwind_plan, this, thread, verbose);
246 UnwindPlan::Row::Row()
247 : m_offset(0), m_cfa_value(), m_afa_value(), m_register_locations() {}
249 bool UnwindPlan::Row::GetRegisterInfo(
251 UnwindPlan::Row::RegisterLocation ®ister_location) const {
252 collection::const_iterator pos = m_register_locations.find(reg_num);
253 if (pos != m_register_locations.end()) {
254 register_location = pos->second;
260 void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) {
261 collection::const_iterator pos = m_register_locations.find(reg_num);
262 if (pos != m_register_locations.end()) {
263 m_register_locations.erase(pos);
267 void UnwindPlan::Row::SetRegisterInfo(
269 const UnwindPlan::Row::RegisterLocation register_location) {
270 m_register_locations[reg_num] = register_location;
273 bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,
277 m_register_locations.find(reg_num) != m_register_locations.end())
279 RegisterLocation reg_loc;
280 reg_loc.SetAtCFAPlusOffset(offset);
281 m_register_locations[reg_num] = reg_loc;
285 bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,
289 m_register_locations.find(reg_num) != m_register_locations.end())
291 RegisterLocation reg_loc;
292 reg_loc.SetIsCFAPlusOffset(offset);
293 m_register_locations[reg_num] = reg_loc;
297 bool UnwindPlan::Row::SetRegisterLocationToUndefined(
298 uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) {
299 collection::iterator pos = m_register_locations.find(reg_num);
300 collection::iterator end = m_register_locations.end();
305 if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())
308 RegisterLocation reg_loc;
309 reg_loc.SetUndefined();
310 m_register_locations[reg_num] = reg_loc;
314 bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num,
317 m_register_locations.find(reg_num) != m_register_locations.end())
319 RegisterLocation reg_loc;
320 reg_loc.SetUnspecified();
321 m_register_locations[reg_num] = reg_loc;
325 bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num,
326 uint32_t other_reg_num,
329 m_register_locations.find(reg_num) != m_register_locations.end())
331 RegisterLocation reg_loc;
332 reg_loc.SetInRegister(other_reg_num);
333 m_register_locations[reg_num] = reg_loc;
337 bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,
340 m_register_locations.find(reg_num) == m_register_locations.end())
342 RegisterLocation reg_loc;
344 m_register_locations[reg_num] = reg_loc;
348 bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const {
349 return m_offset == rhs.m_offset &&
350 m_cfa_value == rhs.m_cfa_value &&
351 m_afa_value == rhs.m_afa_value &&
352 m_register_locations == rhs.m_register_locations;
355 void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) {
356 if (m_row_list.empty() ||
357 m_row_list.back()->GetOffset() != row_sp->GetOffset())
358 m_row_list.push_back(row_sp);
360 m_row_list.back() = row_sp;
363 void UnwindPlan::InsertRow(const UnwindPlan::RowSP &row_sp,
364 bool replace_existing) {
365 collection::iterator it = m_row_list.begin();
366 while (it != m_row_list.end()) {
368 if (row->GetOffset() >= row_sp->GetOffset())
372 if (it == m_row_list.end() || (*it)->GetOffset() != row_sp->GetOffset())
373 m_row_list.insert(it, row_sp);
374 else if (replace_existing)
378 UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const {
380 if (!m_row_list.empty()) {
382 row = m_row_list.back();
384 collection::const_iterator pos, end = m_row_list.end();
385 for (pos = m_row_list.begin(); pos != end; ++pos) {
386 if ((*pos)->GetOffset() <= static_cast<lldb::offset_t>(offset))
396 bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
397 return idx < m_row_list.size();
400 const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const {
401 if (idx < m_row_list.size())
402 return m_row_list[idx];
404 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
406 log->Printf("error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index "
407 "(number rows is %u)",
408 idx, (uint32_t)m_row_list.size());
409 return UnwindPlan::RowSP();
413 const UnwindPlan::RowSP UnwindPlan::GetLastRow() const {
414 if (m_row_list.empty()) {
415 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
417 log->Printf("UnwindPlan::GetLastRow() when rows are empty");
418 return UnwindPlan::RowSP();
420 return m_row_list.back();
423 int UnwindPlan::GetRowCount() const { return m_row_list.size(); }
425 void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) {
426 if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
427 m_plan_valid_address_range = range;
430 bool UnwindPlan::PlanValidAtAddress(Address addr) {
431 // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
432 if (GetRowCount() == 0) {
433 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
436 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
437 log->Printf("UnwindPlan is invalid -- no unwind rows for UnwindPlan "
438 "'%s' at address %s",
439 m_source_name.GetCString(), s.GetData());
442 "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
443 m_source_name.GetCString());
449 // If the 0th Row of unwind instructions is missing, or if it doesn't provide
450 // a register to use to find the Canonical Frame Address, this is not a valid
452 if (GetRowAtIndex(0).get() == nullptr ||
453 GetRowAtIndex(0)->GetCFAValue().GetValueType() ==
454 Row::FAValue::unspecified) {
455 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
458 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
459 log->Printf("UnwindPlan is invalid -- no CFA register defined in row 0 "
460 "for UnwindPlan '%s' at address %s",
461 m_source_name.GetCString(), s.GetData());
463 log->Printf("UnwindPlan is invalid -- no CFA register defined in row 0 "
464 "for UnwindPlan '%s'",
465 m_source_name.GetCString());
471 if (!m_plan_valid_address_range.GetBaseAddress().IsValid() ||
472 m_plan_valid_address_range.GetByteSize() == 0)
478 if (m_plan_valid_address_range.ContainsFileAddress(addr))
484 void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {
485 if (!m_source_name.IsEmpty()) {
486 s.Printf("This UnwindPlan originally sourced from %s\n",
487 m_source_name.GetCString());
489 if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) {
490 TargetSP target_sp(thread->CalculateTarget());
491 addr_t lsda_load_addr = m_lsda_address.GetLoadAddress(target_sp.get());
492 addr_t personality_func_load_addr =
493 m_personality_func_addr.GetLoadAddress(target_sp.get());
495 if (lsda_load_addr != LLDB_INVALID_ADDRESS &&
496 personality_func_load_addr != LLDB_INVALID_ADDRESS) {
497 s.Printf("LSDA address 0x%" PRIx64
498 ", personality routine is at address 0x%" PRIx64 "\n",
499 lsda_load_addr, personality_func_load_addr);
502 s.Printf("This UnwindPlan is sourced from the compiler: ");
503 switch (m_plan_is_sourced_from_compiler) {
510 case eLazyBoolCalculate:
511 s.Printf("not specified.\n");
514 s.Printf("This UnwindPlan is valid at all instruction locations: ");
515 switch (m_plan_is_valid_at_all_instruction_locations) {
522 case eLazyBoolCalculate:
523 s.Printf("not specified.\n");
526 if (m_plan_valid_address_range.GetBaseAddress().IsValid() &&
527 m_plan_valid_address_range.GetByteSize() > 0) {
528 s.PutCString("Address range of this UnwindPlan: ");
529 TargetSP target_sp(thread->CalculateTarget());
530 m_plan_valid_address_range.Dump(&s, target_sp.get(),
531 Address::DumpStyleSectionNameOffset);
534 collection::const_iterator pos, begin = m_row_list.begin(),
535 end = m_row_list.end();
536 for (pos = begin; pos != end; ++pos) {
537 s.Printf("row[%u]: ", (uint32_t)std::distance(begin, pos));
538 (*pos)->Dump(s, this, thread, base_addr);
542 void UnwindPlan::SetSourceName(const char *source) {
543 m_source_name = ConstString(source);
546 ConstString UnwindPlan::GetSourceName() const { return m_source_name; }
548 const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,
549 uint32_t unwind_reg) const {
551 RegisterContext *reg_ctx = thread->GetRegisterContext().get();
554 if (m_register_kind == eRegisterKindLLDB)
557 reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,
559 if (reg != LLDB_INVALID_REGNUM)
560 return reg_ctx->GetRegisterInfoAtIndex(reg);