1 //===-- UnwindPlan.cpp ----------------------------------------------------===//
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/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/DebugInfo/DIContext.h"
19 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
23 using namespace lldb_private
;
25 bool UnwindPlan::Row::AbstractRegisterLocation::operator==(
26 const UnwindPlan::Row::AbstractRegisterLocation
&rhs
) const {
27 if (m_type
== rhs
.m_type
) {
38 return m_location
.offset
== rhs
.m_location
.offset
;
41 return m_location
.reg_num
== rhs
.m_location
.reg_num
;
43 case atDWARFExpression
:
44 case isDWARFExpression
:
45 if (m_location
.expr
.length
== rhs
.m_location
.expr
.length
)
46 return !memcmp(m_location
.expr
.opcodes
, rhs
.m_location
.expr
.opcodes
,
47 m_location
.expr
.length
);
50 return m_location
.constant_value
== rhs
.m_location
.constant_value
;
56 // This function doesn't copy the dwarf expression bytes; they must remain in
57 // allocated memory for the lifespan of this UnwindPlan object.
58 void UnwindPlan::Row::AbstractRegisterLocation::SetAtDWARFExpression(
59 const uint8_t *opcodes
, uint32_t len
) {
60 m_type
= atDWARFExpression
;
61 m_location
.expr
.opcodes
= opcodes
;
62 m_location
.expr
.length
= len
;
65 // This function doesn't copy the dwarf expression bytes; they must remain in
66 // allocated memory for the lifespan of this UnwindPlan object.
67 void UnwindPlan::Row::AbstractRegisterLocation::SetIsDWARFExpression(
68 const uint8_t *opcodes
, uint32_t len
) {
69 m_type
= isDWARFExpression
;
70 m_location
.expr
.opcodes
= opcodes
;
71 m_location
.expr
.length
= len
;
74 static std::optional
<std::pair
<lldb::ByteOrder
, uint32_t>>
75 GetByteOrderAndAddrSize(Thread
*thread
) {
78 ProcessSP process_sp
= thread
->GetProcess();
81 ArchSpec arch
= process_sp
->GetTarget().GetArchitecture();
82 return std::make_pair(arch
.GetByteOrder(), arch
.GetAddressByteSize());
85 static void DumpDWARFExpr(Stream
&s
, llvm::ArrayRef
<uint8_t> expr
, Thread
*thread
) {
86 if (auto order_and_width
= GetByteOrderAndAddrSize(thread
)) {
87 llvm::DataExtractor
data(expr
, order_and_width
->first
== eByteOrderLittle
,
88 order_and_width
->second
);
89 llvm::DWARFExpression(data
, order_and_width
->second
, llvm::dwarf::DWARF32
)
90 .print(s
.AsRawOstream(), llvm::DIDumpOptions(), nullptr);
92 s
.PutCString("dwarf-expr");
95 void UnwindPlan::Row::AbstractRegisterLocation::Dump(
96 Stream
&s
, const UnwindPlan
*unwind_plan
, const UnwindPlan::Row
*row
,
97 Thread
*thread
, bool verbose
) const {
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::ArrayRef(m_location
.expr
.opcodes
, m_location
.expr
.length
),
153 if (m_type
== atDWARFExpression
)
157 s
.Printf("=0x%" PRIx64
, m_location
.constant_value
);
162 static void DumpRegisterName(Stream
&s
, const UnwindPlan
*unwind_plan
,
163 Thread
*thread
, uint32_t reg_num
) {
164 const RegisterInfo
*reg_info
= unwind_plan
->GetRegisterInfo(thread
, reg_num
);
166 s
.PutCString(reg_info
->name
);
168 s
.Printf("reg(%u)", reg_num
);
171 bool UnwindPlan::Row::FAValue::
172 operator==(const UnwindPlan::Row::FAValue
&rhs
) const {
173 if (m_type
== rhs
.m_type
) {
177 return m_value
.ra_search_offset
== rhs
.m_value
.ra_search_offset
;
179 case isRegisterPlusOffset
:
180 return m_value
.reg
.offset
== rhs
.m_value
.reg
.offset
;
182 case isRegisterDereferenced
:
183 return m_value
.reg
.reg_num
== rhs
.m_value
.reg
.reg_num
;
185 case isDWARFExpression
:
186 if (m_value
.expr
.length
== rhs
.m_value
.expr
.length
)
187 return !memcmp(m_value
.expr
.opcodes
, rhs
.m_value
.expr
.opcodes
,
188 m_value
.expr
.length
);
191 return m_value
.constant
== rhs
.m_value
.constant
;
197 void UnwindPlan::Row::FAValue::Dump(Stream
&s
, const UnwindPlan
*unwind_plan
,
198 Thread
*thread
) const {
200 case isRegisterPlusOffset
:
201 DumpRegisterName(s
, unwind_plan
, thread
, m_value
.reg
.reg_num
);
202 s
.Printf("%+3d", m_value
.reg
.offset
);
204 case isRegisterDereferenced
:
206 DumpRegisterName(s
, unwind_plan
, thread
, m_value
.reg
.reg_num
);
209 case isDWARFExpression
:
210 DumpDWARFExpr(s
, llvm::ArrayRef(m_value
.expr
.opcodes
, m_value
.expr
.length
),
214 s
.PutCString("unspecified");
217 s
.Printf("RaSearch@SP%+d", m_value
.ra_search_offset
);
220 s
.Printf("0x%" PRIx64
, m_value
.constant
);
224 void UnwindPlan::Row::Clear() {
225 m_cfa_value
.SetUnspecified();
226 m_afa_value
.SetUnspecified();
228 m_unspecified_registers_are_undefined
= false;
229 m_register_locations
.clear();
232 void UnwindPlan::Row::Dump(Stream
&s
, const UnwindPlan
*unwind_plan
,
233 Thread
*thread
, addr_t base_addr
) const {
234 if (base_addr
!= LLDB_INVALID_ADDRESS
)
235 s
.Printf("0x%16.16" PRIx64
": CFA=", base_addr
+ GetOffset());
237 s
.Printf("%4" PRId64
": CFA=", GetOffset());
239 m_cfa_value
.Dump(s
, unwind_plan
, thread
);
241 if (!m_afa_value
.IsUnspecified()) {
243 m_afa_value
.Dump(s
, unwind_plan
, thread
);
247 for (collection::const_iterator idx
= m_register_locations
.begin();
248 idx
!= m_register_locations
.end(); ++idx
) {
249 DumpRegisterName(s
, unwind_plan
, thread
, idx
->first
);
250 const bool verbose
= false;
251 idx
->second
.Dump(s
, unwind_plan
, this, thread
, verbose
);
256 UnwindPlan::Row::Row() : m_cfa_value(), m_afa_value(), m_register_locations() {}
258 bool UnwindPlan::Row::GetRegisterInfo(
260 UnwindPlan::Row::AbstractRegisterLocation
®ister_location
) const {
261 collection::const_iterator pos
= m_register_locations
.find(reg_num
);
262 if (pos
!= m_register_locations
.end()) {
263 register_location
= pos
->second
;
266 if (m_unspecified_registers_are_undefined
) {
267 register_location
.SetUndefined();
273 void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num
) {
274 collection::const_iterator pos
= m_register_locations
.find(reg_num
);
275 if (pos
!= m_register_locations
.end()) {
276 m_register_locations
.erase(pos
);
280 void UnwindPlan::Row::SetRegisterInfo(
282 const UnwindPlan::Row::AbstractRegisterLocation register_location
) {
283 m_register_locations
[reg_num
] = register_location
;
286 bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num
,
290 m_register_locations
.find(reg_num
) != m_register_locations
.end())
292 AbstractRegisterLocation reg_loc
;
293 reg_loc
.SetAtCFAPlusOffset(offset
);
294 m_register_locations
[reg_num
] = reg_loc
;
298 bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num
,
302 m_register_locations
.find(reg_num
) != m_register_locations
.end())
304 AbstractRegisterLocation reg_loc
;
305 reg_loc
.SetIsCFAPlusOffset(offset
);
306 m_register_locations
[reg_num
] = reg_loc
;
310 bool UnwindPlan::Row::SetRegisterLocationToUndefined(
311 uint32_t reg_num
, bool can_replace
, bool can_replace_only_if_unspecified
) {
312 collection::iterator pos
= m_register_locations
.find(reg_num
);
313 collection::iterator end
= m_register_locations
.end();
318 if (can_replace_only_if_unspecified
&& !pos
->second
.IsUnspecified())
321 AbstractRegisterLocation reg_loc
;
322 reg_loc
.SetUndefined();
323 m_register_locations
[reg_num
] = reg_loc
;
327 bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num
,
330 m_register_locations
.find(reg_num
) != m_register_locations
.end())
332 AbstractRegisterLocation reg_loc
;
333 reg_loc
.SetUnspecified();
334 m_register_locations
[reg_num
] = reg_loc
;
338 bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num
,
339 uint32_t other_reg_num
,
342 m_register_locations
.find(reg_num
) != m_register_locations
.end())
344 AbstractRegisterLocation reg_loc
;
345 reg_loc
.SetInRegister(other_reg_num
);
346 m_register_locations
[reg_num
] = reg_loc
;
350 bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num
,
353 m_register_locations
.find(reg_num
) == m_register_locations
.end())
355 AbstractRegisterLocation reg_loc
;
357 m_register_locations
[reg_num
] = reg_loc
;
361 bool UnwindPlan::Row::SetRegisterLocationToIsDWARFExpression(
362 uint32_t reg_num
, const uint8_t *opcodes
, uint32_t len
, bool can_replace
) {
364 m_register_locations
.find(reg_num
) != m_register_locations
.end())
366 AbstractRegisterLocation reg_loc
;
367 reg_loc
.SetIsDWARFExpression(opcodes
, len
);
368 m_register_locations
[reg_num
] = reg_loc
;
372 bool UnwindPlan::Row::SetRegisterLocationToIsConstant(uint32_t reg_num
,
376 m_register_locations
.find(reg_num
) != m_register_locations
.end())
378 AbstractRegisterLocation reg_loc
;
379 reg_loc
.SetIsConstant(constant
);
380 m_register_locations
[reg_num
] = reg_loc
;
384 bool UnwindPlan::Row::operator==(const UnwindPlan::Row
&rhs
) const {
385 return m_offset
== rhs
.m_offset
&& m_cfa_value
== rhs
.m_cfa_value
&&
386 m_afa_value
== rhs
.m_afa_value
&&
387 m_unspecified_registers_are_undefined
==
388 rhs
.m_unspecified_registers_are_undefined
&&
389 m_register_locations
== rhs
.m_register_locations
;
392 void UnwindPlan::AppendRow(const UnwindPlan::RowSP
&row_sp
) {
393 if (m_row_list
.empty() ||
394 m_row_list
.back()->GetOffset() != row_sp
->GetOffset())
395 m_row_list
.push_back(row_sp
);
397 m_row_list
.back() = row_sp
;
400 void UnwindPlan::InsertRow(const UnwindPlan::RowSP
&row_sp
,
401 bool replace_existing
) {
402 collection::iterator it
= m_row_list
.begin();
403 while (it
!= m_row_list
.end()) {
405 if (row
->GetOffset() >= row_sp
->GetOffset())
409 if (it
== m_row_list
.end() || (*it
)->GetOffset() != row_sp
->GetOffset())
410 m_row_list
.insert(it
, row_sp
);
411 else if (replace_existing
)
415 UnwindPlan::RowSP
UnwindPlan::GetRowForFunctionOffset(int offset
) const {
417 if (!m_row_list
.empty()) {
419 row
= m_row_list
.back();
421 collection::const_iterator pos
, end
= m_row_list
.end();
422 for (pos
= m_row_list
.begin(); pos
!= end
; ++pos
) {
423 if ((*pos
)->GetOffset() <= static_cast<lldb::offset_t
>(offset
))
433 bool UnwindPlan::IsValidRowIndex(uint32_t idx
) const {
434 return idx
< m_row_list
.size();
437 const UnwindPlan::RowSP
UnwindPlan::GetRowAtIndex(uint32_t idx
) const {
438 if (idx
< m_row_list
.size())
439 return m_row_list
[idx
];
441 Log
*log
= GetLog(LLDBLog::Unwind
);
443 "error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index "
444 "(number rows is %u)",
445 idx
, (uint32_t)m_row_list
.size());
446 return UnwindPlan::RowSP();
450 const UnwindPlan::RowSP
UnwindPlan::GetLastRow() const {
451 if (m_row_list
.empty()) {
452 Log
*log
= GetLog(LLDBLog::Unwind
);
453 LLDB_LOGF(log
, "UnwindPlan::GetLastRow() when rows are empty");
454 return UnwindPlan::RowSP();
456 return m_row_list
.back();
459 int UnwindPlan::GetRowCount() const { return m_row_list
.size(); }
461 void UnwindPlan::SetPlanValidAddressRange(const AddressRange
&range
) {
462 if (range
.GetBaseAddress().IsValid() && range
.GetByteSize() != 0)
463 m_plan_valid_address_range
= range
;
466 bool UnwindPlan::PlanValidAtAddress(Address addr
) {
467 // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
468 if (GetRowCount() == 0) {
469 Log
*log
= GetLog(LLDBLog::Unwind
);
472 if (addr
.Dump(&s
, nullptr, Address::DumpStyleSectionNameOffset
)) {
474 "UnwindPlan is invalid -- no unwind rows for UnwindPlan "
475 "'%s' at address %s",
476 m_source_name
.GetCString(), s
.GetData());
479 "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
480 m_source_name
.GetCString());
486 // If the 0th Row of unwind instructions is missing, or if it doesn't provide
487 // a register to use to find the Canonical Frame Address, this is not a valid
489 if (GetRowAtIndex(0).get() == nullptr ||
490 GetRowAtIndex(0)->GetCFAValue().GetValueType() ==
491 Row::FAValue::unspecified
) {
492 Log
*log
= GetLog(LLDBLog::Unwind
);
495 if (addr
.Dump(&s
, nullptr, Address::DumpStyleSectionNameOffset
)) {
497 "UnwindPlan is invalid -- no CFA register defined in row 0 "
498 "for UnwindPlan '%s' at address %s",
499 m_source_name
.GetCString(), s
.GetData());
502 "UnwindPlan is invalid -- no CFA register defined in row 0 "
503 "for UnwindPlan '%s'",
504 m_source_name
.GetCString());
510 if (!m_plan_valid_address_range
.GetBaseAddress().IsValid() ||
511 m_plan_valid_address_range
.GetByteSize() == 0)
517 if (m_plan_valid_address_range
.ContainsFileAddress(addr
))
523 void UnwindPlan::Dump(Stream
&s
, Thread
*thread
, lldb::addr_t base_addr
) const {
524 if (!m_source_name
.IsEmpty()) {
525 s
.Printf("This UnwindPlan originally sourced from %s\n",
526 m_source_name
.GetCString());
528 if (m_lsda_address
.IsValid() && m_personality_func_addr
.IsValid()) {
529 TargetSP
target_sp(thread
->CalculateTarget());
530 addr_t lsda_load_addr
= m_lsda_address
.GetLoadAddress(target_sp
.get());
531 addr_t personality_func_load_addr
=
532 m_personality_func_addr
.GetLoadAddress(target_sp
.get());
534 if (lsda_load_addr
!= LLDB_INVALID_ADDRESS
&&
535 personality_func_load_addr
!= LLDB_INVALID_ADDRESS
) {
536 s
.Printf("LSDA address 0x%" PRIx64
537 ", personality routine is at address 0x%" PRIx64
"\n",
538 lsda_load_addr
, personality_func_load_addr
);
541 s
.Printf("This UnwindPlan is sourced from the compiler: ");
542 switch (m_plan_is_sourced_from_compiler
) {
549 case eLazyBoolCalculate
:
550 s
.Printf("not specified.\n");
553 s
.Printf("This UnwindPlan is valid at all instruction locations: ");
554 switch (m_plan_is_valid_at_all_instruction_locations
) {
561 case eLazyBoolCalculate
:
562 s
.Printf("not specified.\n");
565 s
.Printf("This UnwindPlan is for a trap handler function: ");
566 switch (m_plan_is_for_signal_trap
) {
573 case eLazyBoolCalculate
:
574 s
.Printf("not specified.\n");
577 if (m_plan_valid_address_range
.GetBaseAddress().IsValid() &&
578 m_plan_valid_address_range
.GetByteSize() > 0) {
579 s
.PutCString("Address range of this UnwindPlan: ");
580 TargetSP
target_sp(thread
->CalculateTarget());
581 m_plan_valid_address_range
.Dump(&s
, target_sp
.get(),
582 Address::DumpStyleSectionNameOffset
);
585 collection::const_iterator pos
, begin
= m_row_list
.begin(),
586 end
= m_row_list
.end();
587 for (pos
= begin
; pos
!= end
; ++pos
) {
588 s
.Printf("row[%u]: ", (uint32_t)std::distance(begin
, pos
));
589 (*pos
)->Dump(s
, this, thread
, base_addr
);
594 void UnwindPlan::SetSourceName(const char *source
) {
595 m_source_name
= ConstString(source
);
598 ConstString
UnwindPlan::GetSourceName() const { return m_source_name
; }
600 const RegisterInfo
*UnwindPlan::GetRegisterInfo(Thread
*thread
,
601 uint32_t unwind_reg
) const {
603 RegisterContext
*reg_ctx
= thread
->GetRegisterContext().get();
606 if (m_register_kind
== eRegisterKindLLDB
)
609 reg
= reg_ctx
->ConvertRegisterKindToRegisterNumber(m_register_kind
,
611 if (reg
!= LLDB_INVALID_REGNUM
)
612 return reg_ctx
->GetRegisterInfoAtIndex(reg
);