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::RegisterLocation::
26 operator==(const UnwindPlan::Row::RegisterLocation
&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
);
54 // This function doesn't copy the dwarf expression bytes; they must remain in
55 // allocated memory for the lifespan of this UnwindPlan object.
56 void UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression(
57 const uint8_t *opcodes
, uint32_t len
) {
58 m_type
= atDWARFExpression
;
59 m_location
.expr
.opcodes
= opcodes
;
60 m_location
.expr
.length
= len
;
63 // This function doesn't copy the dwarf expression bytes; they must remain in
64 // allocated memory for the lifespan of this UnwindPlan object.
65 void UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression(
66 const uint8_t *opcodes
, uint32_t len
) {
67 m_type
= isDWARFExpression
;
68 m_location
.expr
.opcodes
= opcodes
;
69 m_location
.expr
.length
= len
;
72 static std::optional
<std::pair
<lldb::ByteOrder
, uint32_t>>
73 GetByteOrderAndAddrSize(Thread
*thread
) {
76 ProcessSP process_sp
= thread
->GetProcess();
79 ArchSpec arch
= process_sp
->GetTarget().GetArchitecture();
80 return std::make_pair(arch
.GetByteOrder(), arch
.GetAddressByteSize());
83 static void DumpDWARFExpr(Stream
&s
, llvm::ArrayRef
<uint8_t> expr
, Thread
*thread
) {
84 if (auto order_and_width
= GetByteOrderAndAddrSize(thread
)) {
85 llvm::DataExtractor
data(expr
, order_and_width
->first
== eByteOrderLittle
,
86 order_and_width
->second
);
87 llvm::DWARFExpression(data
, order_and_width
->second
, llvm::dwarf::DWARF32
)
88 .print(s
.AsRawOstream(), llvm::DIDumpOptions(), nullptr);
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::ArrayRef(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
) {
174 return m_value
.ra_search_offset
== rhs
.m_value
.ra_search_offset
;
176 case isRegisterPlusOffset
:
177 return m_value
.reg
.offset
== rhs
.m_value
.reg
.offset
;
179 case isRegisterDereferenced
:
180 return m_value
.reg
.reg_num
== rhs
.m_value
.reg
.reg_num
;
182 case isDWARFExpression
:
183 if (m_value
.expr
.length
== rhs
.m_value
.expr
.length
)
184 return !memcmp(m_value
.expr
.opcodes
, rhs
.m_value
.expr
.opcodes
,
185 m_value
.expr
.length
);
192 void UnwindPlan::Row::FAValue::Dump(Stream
&s
, const UnwindPlan
*unwind_plan
,
193 Thread
*thread
) const {
195 case isRegisterPlusOffset
:
196 DumpRegisterName(s
, unwind_plan
, thread
, m_value
.reg
.reg_num
);
197 s
.Printf("%+3d", m_value
.reg
.offset
);
199 case isRegisterDereferenced
:
201 DumpRegisterName(s
, unwind_plan
, thread
, m_value
.reg
.reg_num
);
204 case isDWARFExpression
:
205 DumpDWARFExpr(s
, llvm::ArrayRef(m_value
.expr
.opcodes
, m_value
.expr
.length
),
209 s
.PutCString("unspecified");
212 s
.Printf("RaSearch@SP%+d", m_value
.ra_search_offset
);
217 void UnwindPlan::Row::Clear() {
218 m_cfa_value
.SetUnspecified();
219 m_afa_value
.SetUnspecified();
221 m_unspecified_registers_are_undefined
= false;
222 m_register_locations
.clear();
225 void UnwindPlan::Row::Dump(Stream
&s
, const UnwindPlan
*unwind_plan
,
226 Thread
*thread
, addr_t base_addr
) const {
227 if (base_addr
!= LLDB_INVALID_ADDRESS
)
228 s
.Printf("0x%16.16" PRIx64
": CFA=", base_addr
+ GetOffset());
230 s
.Printf("%4" PRId64
": CFA=", GetOffset());
232 m_cfa_value
.Dump(s
, unwind_plan
, thread
);
234 if (!m_afa_value
.IsUnspecified()) {
236 m_afa_value
.Dump(s
, unwind_plan
, thread
);
240 for (collection::const_iterator idx
= m_register_locations
.begin();
241 idx
!= m_register_locations
.end(); ++idx
) {
242 DumpRegisterName(s
, unwind_plan
, thread
, idx
->first
);
243 const bool verbose
= false;
244 idx
->second
.Dump(s
, unwind_plan
, this, thread
, verbose
);
249 UnwindPlan::Row::Row() : m_cfa_value(), m_afa_value(), m_register_locations() {}
251 bool UnwindPlan::Row::GetRegisterInfo(
253 UnwindPlan::Row::RegisterLocation
®ister_location
) const {
254 collection::const_iterator pos
= m_register_locations
.find(reg_num
);
255 if (pos
!= m_register_locations
.end()) {
256 register_location
= pos
->second
;
259 if (m_unspecified_registers_are_undefined
) {
260 register_location
.SetUndefined();
266 void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num
) {
267 collection::const_iterator pos
= m_register_locations
.find(reg_num
);
268 if (pos
!= m_register_locations
.end()) {
269 m_register_locations
.erase(pos
);
273 void UnwindPlan::Row::SetRegisterInfo(
275 const UnwindPlan::Row::RegisterLocation register_location
) {
276 m_register_locations
[reg_num
] = register_location
;
279 bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num
,
283 m_register_locations
.find(reg_num
) != m_register_locations
.end())
285 RegisterLocation reg_loc
;
286 reg_loc
.SetAtCFAPlusOffset(offset
);
287 m_register_locations
[reg_num
] = reg_loc
;
291 bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num
,
295 m_register_locations
.find(reg_num
) != m_register_locations
.end())
297 RegisterLocation reg_loc
;
298 reg_loc
.SetIsCFAPlusOffset(offset
);
299 m_register_locations
[reg_num
] = reg_loc
;
303 bool UnwindPlan::Row::SetRegisterLocationToUndefined(
304 uint32_t reg_num
, bool can_replace
, bool can_replace_only_if_unspecified
) {
305 collection::iterator pos
= m_register_locations
.find(reg_num
);
306 collection::iterator end
= m_register_locations
.end();
311 if (can_replace_only_if_unspecified
&& !pos
->second
.IsUnspecified())
314 RegisterLocation reg_loc
;
315 reg_loc
.SetUndefined();
316 m_register_locations
[reg_num
] = reg_loc
;
320 bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num
,
323 m_register_locations
.find(reg_num
) != m_register_locations
.end())
325 RegisterLocation reg_loc
;
326 reg_loc
.SetUnspecified();
327 m_register_locations
[reg_num
] = reg_loc
;
331 bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num
,
332 uint32_t other_reg_num
,
335 m_register_locations
.find(reg_num
) != m_register_locations
.end())
337 RegisterLocation reg_loc
;
338 reg_loc
.SetInRegister(other_reg_num
);
339 m_register_locations
[reg_num
] = reg_loc
;
343 bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num
,
346 m_register_locations
.find(reg_num
) == m_register_locations
.end())
348 RegisterLocation reg_loc
;
350 m_register_locations
[reg_num
] = reg_loc
;
354 bool UnwindPlan::Row::operator==(const UnwindPlan::Row
&rhs
) const {
355 return m_offset
== rhs
.m_offset
&& m_cfa_value
== rhs
.m_cfa_value
&&
356 m_afa_value
== rhs
.m_afa_value
&&
357 m_unspecified_registers_are_undefined
==
358 rhs
.m_unspecified_registers_are_undefined
&&
359 m_register_locations
== rhs
.m_register_locations
;
362 void UnwindPlan::AppendRow(const UnwindPlan::RowSP
&row_sp
) {
363 if (m_row_list
.empty() ||
364 m_row_list
.back()->GetOffset() != row_sp
->GetOffset())
365 m_row_list
.push_back(row_sp
);
367 m_row_list
.back() = row_sp
;
370 void UnwindPlan::InsertRow(const UnwindPlan::RowSP
&row_sp
,
371 bool replace_existing
) {
372 collection::iterator it
= m_row_list
.begin();
373 while (it
!= m_row_list
.end()) {
375 if (row
->GetOffset() >= row_sp
->GetOffset())
379 if (it
== m_row_list
.end() || (*it
)->GetOffset() != row_sp
->GetOffset())
380 m_row_list
.insert(it
, row_sp
);
381 else if (replace_existing
)
385 UnwindPlan::RowSP
UnwindPlan::GetRowForFunctionOffset(int offset
) const {
387 if (!m_row_list
.empty()) {
389 row
= m_row_list
.back();
391 collection::const_iterator pos
, end
= m_row_list
.end();
392 for (pos
= m_row_list
.begin(); pos
!= end
; ++pos
) {
393 if ((*pos
)->GetOffset() <= static_cast<lldb::offset_t
>(offset
))
403 bool UnwindPlan::IsValidRowIndex(uint32_t idx
) const {
404 return idx
< m_row_list
.size();
407 const UnwindPlan::RowSP
UnwindPlan::GetRowAtIndex(uint32_t idx
) const {
408 if (idx
< m_row_list
.size())
409 return m_row_list
[idx
];
411 Log
*log
= GetLog(LLDBLog::Unwind
);
413 "error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index "
414 "(number rows is %u)",
415 idx
, (uint32_t)m_row_list
.size());
416 return UnwindPlan::RowSP();
420 const UnwindPlan::RowSP
UnwindPlan::GetLastRow() const {
421 if (m_row_list
.empty()) {
422 Log
*log
= GetLog(LLDBLog::Unwind
);
423 LLDB_LOGF(log
, "UnwindPlan::GetLastRow() when rows are empty");
424 return UnwindPlan::RowSP();
426 return m_row_list
.back();
429 int UnwindPlan::GetRowCount() const { return m_row_list
.size(); }
431 void UnwindPlan::SetPlanValidAddressRange(const AddressRange
&range
) {
432 if (range
.GetBaseAddress().IsValid() && range
.GetByteSize() != 0)
433 m_plan_valid_address_range
= range
;
436 bool UnwindPlan::PlanValidAtAddress(Address addr
) {
437 // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
438 if (GetRowCount() == 0) {
439 Log
*log
= GetLog(LLDBLog::Unwind
);
442 if (addr
.Dump(&s
, nullptr, Address::DumpStyleSectionNameOffset
)) {
444 "UnwindPlan is invalid -- no unwind rows for UnwindPlan "
445 "'%s' at address %s",
446 m_source_name
.GetCString(), s
.GetData());
449 "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
450 m_source_name
.GetCString());
456 // If the 0th Row of unwind instructions is missing, or if it doesn't provide
457 // a register to use to find the Canonical Frame Address, this is not a valid
459 if (GetRowAtIndex(0).get() == nullptr ||
460 GetRowAtIndex(0)->GetCFAValue().GetValueType() ==
461 Row::FAValue::unspecified
) {
462 Log
*log
= GetLog(LLDBLog::Unwind
);
465 if (addr
.Dump(&s
, nullptr, Address::DumpStyleSectionNameOffset
)) {
467 "UnwindPlan is invalid -- no CFA register defined in row 0 "
468 "for UnwindPlan '%s' at address %s",
469 m_source_name
.GetCString(), s
.GetData());
472 "UnwindPlan is invalid -- no CFA register defined in row 0 "
473 "for UnwindPlan '%s'",
474 m_source_name
.GetCString());
480 if (!m_plan_valid_address_range
.GetBaseAddress().IsValid() ||
481 m_plan_valid_address_range
.GetByteSize() == 0)
487 if (m_plan_valid_address_range
.ContainsFileAddress(addr
))
493 void UnwindPlan::Dump(Stream
&s
, Thread
*thread
, lldb::addr_t base_addr
) const {
494 if (!m_source_name
.IsEmpty()) {
495 s
.Printf("This UnwindPlan originally sourced from %s\n",
496 m_source_name
.GetCString());
498 if (m_lsda_address
.IsValid() && m_personality_func_addr
.IsValid()) {
499 TargetSP
target_sp(thread
->CalculateTarget());
500 addr_t lsda_load_addr
= m_lsda_address
.GetLoadAddress(target_sp
.get());
501 addr_t personality_func_load_addr
=
502 m_personality_func_addr
.GetLoadAddress(target_sp
.get());
504 if (lsda_load_addr
!= LLDB_INVALID_ADDRESS
&&
505 personality_func_load_addr
!= LLDB_INVALID_ADDRESS
) {
506 s
.Printf("LSDA address 0x%" PRIx64
507 ", personality routine is at address 0x%" PRIx64
"\n",
508 lsda_load_addr
, personality_func_load_addr
);
511 s
.Printf("This UnwindPlan is sourced from the compiler: ");
512 switch (m_plan_is_sourced_from_compiler
) {
519 case eLazyBoolCalculate
:
520 s
.Printf("not specified.\n");
523 s
.Printf("This UnwindPlan is valid at all instruction locations: ");
524 switch (m_plan_is_valid_at_all_instruction_locations
) {
531 case eLazyBoolCalculate
:
532 s
.Printf("not specified.\n");
535 s
.Printf("This UnwindPlan is for a trap handler function: ");
536 switch (m_plan_is_for_signal_trap
) {
543 case eLazyBoolCalculate
:
544 s
.Printf("not specified.\n");
547 if (m_plan_valid_address_range
.GetBaseAddress().IsValid() &&
548 m_plan_valid_address_range
.GetByteSize() > 0) {
549 s
.PutCString("Address range of this UnwindPlan: ");
550 TargetSP
target_sp(thread
->CalculateTarget());
551 m_plan_valid_address_range
.Dump(&s
, target_sp
.get(),
552 Address::DumpStyleSectionNameOffset
);
555 collection::const_iterator pos
, begin
= m_row_list
.begin(),
556 end
= m_row_list
.end();
557 for (pos
= begin
; pos
!= end
; ++pos
) {
558 s
.Printf("row[%u]: ", (uint32_t)std::distance(begin
, pos
));
559 (*pos
)->Dump(s
, this, thread
, base_addr
);
564 void UnwindPlan::SetSourceName(const char *source
) {
565 m_source_name
= ConstString(source
);
568 ConstString
UnwindPlan::GetSourceName() const { return m_source_name
; }
570 const RegisterInfo
*UnwindPlan::GetRegisterInfo(Thread
*thread
,
571 uint32_t unwind_reg
) const {
573 RegisterContext
*reg_ctx
= thread
->GetRegisterContext().get();
576 if (m_register_kind
== eRegisterKindLLDB
)
579 reg
= reg_ctx
->ConvertRegisterKindToRegisterNumber(m_register_kind
,
581 if (reg
!= LLDB_INVALID_REGNUM
)
582 return reg_ctx
->GetRegisterInfoAtIndex(reg
);