1 //===-- UnwindPlan.cpp ----------------------------------*- C++ -*-===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "lldb/Symbol/UnwindPlan.h"
10
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"
18
19 using namespace lldb;
20 using namespace lldb_private;
21
22 bool UnwindPlan::Row::RegisterLocation::
operator ==(const UnwindPlan::Row::RegisterLocation & rhs) const23 operator==(const UnwindPlan::Row::RegisterLocation &rhs) const {
24 if (m_type == rhs.m_type) {
25 switch (m_type) {
26 case unspecified:
27 case undefined:
28 case same:
29 return true;
30
31 case atCFAPlusOffset:
32 case isCFAPlusOffset:
33 case atAFAPlusOffset:
34 case isAFAPlusOffset:
35 return m_location.offset == rhs.m_location.offset;
36
37 case inOtherRegister:
38 return m_location.reg_num == rhs.m_location.reg_num;
39
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);
45 break;
46 }
47 }
48 return false;
49 }
50
51 // This function doesn't copy the dwarf expression bytes; they must remain in
52 // allocated memory for the lifespan of this UnwindPlan object.
SetAtDWARFExpression(const uint8_t * opcodes,uint32_t len)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;
58 }
59
60 // This function doesn't copy the dwarf expression bytes; they must remain in
61 // allocated memory for the lifespan of this UnwindPlan object.
SetIsDWARFExpression(const uint8_t * opcodes,uint32_t len)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;
67 }
68
69 static llvm::Optional<std::pair<lldb::ByteOrder, uint32_t>>
GetByteOrderAndAddrSize(Thread * thread)70 GetByteOrderAndAddrSize(Thread *thread) {
71 if (!thread)
72 return llvm::None;
73 ProcessSP process_sp = thread->GetProcess();
74 if (!process_sp)
75 return llvm::None;
76 ArchSpec arch = process_sp->GetTarget().GetArchitecture();
77 return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());
78 }
79
DumpDWARFExpr(Stream & s,llvm::ArrayRef<uint8_t> expr,Thread * thread)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,
86 /*dwarf_ref_size*/ 4,
87 /*location_expression*/ false))
88 s.PutCString("invalid-dwarf-expr");
89 } else
90 s.PutCString("dwarf-expr");
91 }
92
Dump(Stream & s,const UnwindPlan * unwind_plan,const UnwindPlan::Row * row,Thread * thread,bool verbose) const93 void UnwindPlan::Row::RegisterLocation::Dump(Stream &s,
94 const UnwindPlan *unwind_plan,
95 const UnwindPlan::Row *row,
96 Thread *thread,
97 bool verbose) const {
98 switch (m_type) {
99 case unspecified:
100 if (verbose)
101 s.PutCString("=<unspec>");
102 else
103 s.PutCString("=!");
104 break;
105 case undefined:
106 if (verbose)
107 s.PutCString("=<undef>");
108 else
109 s.PutCString("=?");
110 break;
111 case same:
112 s.PutCString("= <same>");
113 break;
114
115 case atCFAPlusOffset:
116 case isCFAPlusOffset: {
117 s.PutChar('=');
118 if (m_type == atCFAPlusOffset)
119 s.PutChar('[');
120 s.Printf("CFA%+d", m_location.offset);
121 if (m_type == atCFAPlusOffset)
122 s.PutChar(']');
123 } break;
124
125 case atAFAPlusOffset:
126 case isAFAPlusOffset: {
127 s.PutChar('=');
128 if (m_type == atAFAPlusOffset)
129 s.PutChar('[');
130 s.Printf("AFA%+d", m_location.offset);
131 if (m_type == atAFAPlusOffset)
132 s.PutChar(']');
133 } break;
134
135 case inOtherRegister: {
136 const RegisterInfo *other_reg_info = nullptr;
137 if (unwind_plan)
138 other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num);
139 if (other_reg_info)
140 s.Printf("=%s", other_reg_info->name);
141 else
142 s.Printf("=reg(%u)", m_location.reg_num);
143 } break;
144
145 case atDWARFExpression:
146 case isDWARFExpression: {
147 s.PutChar('=');
148 if (m_type == atDWARFExpression)
149 s.PutChar('[');
150 DumpDWARFExpr(
151 s, llvm::makeArrayRef(m_location.expr.opcodes, m_location.expr.length),
152 thread);
153 if (m_type == atDWARFExpression)
154 s.PutChar(']');
155 } break;
156 }
157 }
158
DumpRegisterName(Stream & s,const UnwindPlan * unwind_plan,Thread * thread,uint32_t reg_num)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);
162 if (reg_info)
163 s.PutCString(reg_info->name);
164 else
165 s.Printf("reg(%u)", reg_num);
166 }
167
168 bool UnwindPlan::Row::FAValue::
operator ==(const UnwindPlan::Row::FAValue & rhs) const169 operator==(const UnwindPlan::Row::FAValue &rhs) const {
170 if (m_type == rhs.m_type) {
171 switch (m_type) {
172 case unspecified:
173 case isRaSearch:
174 return m_value.ra_search_offset == rhs.m_value.ra_search_offset;
175
176 case isRegisterPlusOffset:
177 return m_value.reg.offset == rhs.m_value.reg.offset;
178
179 case isRegisterDereferenced:
180 return m_value.reg.reg_num == rhs.m_value.reg.reg_num;
181
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);
186 break;
187 }
188 }
189 return false;
190 }
191
Dump(Stream & s,const UnwindPlan * unwind_plan,Thread * thread) const192 void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan,
193 Thread *thread) const {
194 switch (m_type) {
195 case isRegisterPlusOffset:
196 DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
197 s.Printf("%+3d", m_value.reg.offset);
198 break;
199 case isRegisterDereferenced:
200 s.PutChar('[');
201 DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
202 s.PutChar(']');
203 break;
204 case isDWARFExpression:
205 DumpDWARFExpr(s,
206 llvm::makeArrayRef(m_value.expr.opcodes, m_value.expr.length),
207 thread);
208 break;
209 case unspecified:
210 s.PutCString("unspecified");
211 break;
212 case isRaSearch:
213 s.Printf("RaSearch@SP%+d", m_value.ra_search_offset);
214 break;
215 }
216 }
217
Clear()218 void UnwindPlan::Row::Clear() {
219 m_cfa_value.SetUnspecified();
220 m_afa_value.SetUnspecified();
221 m_offset = 0;
222 m_register_locations.clear();
223 }
224
Dump(Stream & s,const UnwindPlan * unwind_plan,Thread * thread,addr_t base_addr) const225 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());
229 else
230 s.Printf("%4" PRId64 ": CFA=", GetOffset());
231
232 m_cfa_value.Dump(s, unwind_plan, thread);
233
234 if (!m_afa_value.IsUnspecified()) {
235 s.Printf(" AFA=");
236 m_afa_value.Dump(s, unwind_plan, thread);
237 }
238
239 s.Printf(" => ");
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);
245 s.PutChar(' ');
246 }
247 s.EOL();
248 }
249
Row()250 UnwindPlan::Row::Row()
251 : m_offset(0), m_cfa_value(), m_afa_value(), m_register_locations() {}
252
GetRegisterInfo(uint32_t reg_num,UnwindPlan::Row::RegisterLocation & register_location) const253 bool UnwindPlan::Row::GetRegisterInfo(
254 uint32_t reg_num,
255 UnwindPlan::Row::RegisterLocation ®ister_location) const {
256 collection::const_iterator pos = m_register_locations.find(reg_num);
257 if (pos != m_register_locations.end()) {
258 register_location = pos->second;
259 return true;
260 }
261 return false;
262 }
263
RemoveRegisterInfo(uint32_t reg_num)264 void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) {
265 collection::const_iterator pos = m_register_locations.find(reg_num);
266 if (pos != m_register_locations.end()) {
267 m_register_locations.erase(pos);
268 }
269 }
270
SetRegisterInfo(uint32_t reg_num,const UnwindPlan::Row::RegisterLocation register_location)271 void UnwindPlan::Row::SetRegisterInfo(
272 uint32_t reg_num,
273 const UnwindPlan::Row::RegisterLocation register_location) {
274 m_register_locations[reg_num] = register_location;
275 }
276
SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,int32_t offset,bool can_replace)277 bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,
278 int32_t offset,
279 bool can_replace) {
280 if (!can_replace &&
281 m_register_locations.find(reg_num) != m_register_locations.end())
282 return false;
283 RegisterLocation reg_loc;
284 reg_loc.SetAtCFAPlusOffset(offset);
285 m_register_locations[reg_num] = reg_loc;
286 return true;
287 }
288
SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,int32_t offset,bool can_replace)289 bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(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 RegisterLocation reg_loc;
296 reg_loc.SetIsCFAPlusOffset(offset);
297 m_register_locations[reg_num] = reg_loc;
298 return true;
299 }
300
SetRegisterLocationToUndefined(uint32_t reg_num,bool can_replace,bool can_replace_only_if_unspecified)301 bool UnwindPlan::Row::SetRegisterLocationToUndefined(
302 uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) {
303 collection::iterator pos = m_register_locations.find(reg_num);
304 collection::iterator end = m_register_locations.end();
305
306 if (pos != end) {
307 if (!can_replace)
308 return false;
309 if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())
310 return false;
311 }
312 RegisterLocation reg_loc;
313 reg_loc.SetUndefined();
314 m_register_locations[reg_num] = reg_loc;
315 return true;
316 }
317
SetRegisterLocationToUnspecified(uint32_t reg_num,bool can_replace)318 bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num,
319 bool can_replace) {
320 if (!can_replace &&
321 m_register_locations.find(reg_num) != m_register_locations.end())
322 return false;
323 RegisterLocation reg_loc;
324 reg_loc.SetUnspecified();
325 m_register_locations[reg_num] = reg_loc;
326 return true;
327 }
328
SetRegisterLocationToRegister(uint32_t reg_num,uint32_t other_reg_num,bool can_replace)329 bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num,
330 uint32_t other_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 RegisterLocation reg_loc;
336 reg_loc.SetInRegister(other_reg_num);
337 m_register_locations[reg_num] = reg_loc;
338 return true;
339 }
340
SetRegisterLocationToSame(uint32_t reg_num,bool must_replace)341 bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,
342 bool must_replace) {
343 if (must_replace &&
344 m_register_locations.find(reg_num) == m_register_locations.end())
345 return false;
346 RegisterLocation reg_loc;
347 reg_loc.SetSame();
348 m_register_locations[reg_num] = reg_loc;
349 return true;
350 }
351
operator ==(const UnwindPlan::Row & rhs) const352 bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const {
353 return m_offset == rhs.m_offset &&
354 m_cfa_value == rhs.m_cfa_value &&
355 m_afa_value == rhs.m_afa_value &&
356 m_register_locations == rhs.m_register_locations;
357 }
358
AppendRow(const UnwindPlan::RowSP & row_sp)359 void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) {
360 if (m_row_list.empty() ||
361 m_row_list.back()->GetOffset() != row_sp->GetOffset())
362 m_row_list.push_back(row_sp);
363 else
364 m_row_list.back() = row_sp;
365 }
366
InsertRow(const UnwindPlan::RowSP & row_sp,bool replace_existing)367 void UnwindPlan::InsertRow(const UnwindPlan::RowSP &row_sp,
368 bool replace_existing) {
369 collection::iterator it = m_row_list.begin();
370 while (it != m_row_list.end()) {
371 RowSP row = *it;
372 if (row->GetOffset() >= row_sp->GetOffset())
373 break;
374 it++;
375 }
376 if (it == m_row_list.end() || (*it)->GetOffset() != row_sp->GetOffset())
377 m_row_list.insert(it, row_sp);
378 else if (replace_existing)
379 *it = row_sp;
380 }
381
GetRowForFunctionOffset(int offset) const382 UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const {
383 RowSP row;
384 if (!m_row_list.empty()) {
385 if (offset == -1)
386 row = m_row_list.back();
387 else {
388 collection::const_iterator pos, end = m_row_list.end();
389 for (pos = m_row_list.begin(); pos != end; ++pos) {
390 if ((*pos)->GetOffset() <= static_cast<lldb::offset_t>(offset))
391 row = *pos;
392 else
393 break;
394 }
395 }
396 }
397 return row;
398 }
399
IsValidRowIndex(uint32_t idx) const400 bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
401 return idx < m_row_list.size();
402 }
403
GetRowAtIndex(uint32_t idx) const404 const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const {
405 if (idx < m_row_list.size())
406 return m_row_list[idx];
407 else {
408 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
409 LLDB_LOGF(log,
410 "error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index "
411 "(number rows is %u)",
412 idx, (uint32_t)m_row_list.size());
413 return UnwindPlan::RowSP();
414 }
415 }
416
GetLastRow() const417 const UnwindPlan::RowSP UnwindPlan::GetLastRow() const {
418 if (m_row_list.empty()) {
419 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
420 LLDB_LOGF(log, "UnwindPlan::GetLastRow() when rows are empty");
421 return UnwindPlan::RowSP();
422 }
423 return m_row_list.back();
424 }
425
GetRowCount() const426 int UnwindPlan::GetRowCount() const { return m_row_list.size(); }
427
SetPlanValidAddressRange(const AddressRange & range)428 void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) {
429 if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
430 m_plan_valid_address_range = range;
431 }
432
PlanValidAtAddress(Address addr)433 bool UnwindPlan::PlanValidAtAddress(Address addr) {
434 // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
435 if (GetRowCount() == 0) {
436 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
437 if (log) {
438 StreamString s;
439 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
440 LLDB_LOGF(log,
441 "UnwindPlan is invalid -- no unwind rows for UnwindPlan "
442 "'%s' at address %s",
443 m_source_name.GetCString(), s.GetData());
444 } else {
445 LLDB_LOGF(log,
446 "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
447 m_source_name.GetCString());
448 }
449 }
450 return false;
451 }
452
453 // If the 0th Row of unwind instructions is missing, or if it doesn't provide
454 // a register to use to find the Canonical Frame Address, this is not a valid
455 // UnwindPlan.
456 if (GetRowAtIndex(0).get() == nullptr ||
457 GetRowAtIndex(0)->GetCFAValue().GetValueType() ==
458 Row::FAValue::unspecified) {
459 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
460 if (log) {
461 StreamString s;
462 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
463 LLDB_LOGF(log,
464 "UnwindPlan is invalid -- no CFA register defined in row 0 "
465 "for UnwindPlan '%s' at address %s",
466 m_source_name.GetCString(), s.GetData());
467 } else {
468 LLDB_LOGF(log,
469 "UnwindPlan is invalid -- no CFA register defined in row 0 "
470 "for UnwindPlan '%s'",
471 m_source_name.GetCString());
472 }
473 }
474 return false;
475 }
476
477 if (!m_plan_valid_address_range.GetBaseAddress().IsValid() ||
478 m_plan_valid_address_range.GetByteSize() == 0)
479 return true;
480
481 if (!addr.IsValid())
482 return true;
483
484 if (m_plan_valid_address_range.ContainsFileAddress(addr))
485 return true;
486
487 return false;
488 }
489
Dump(Stream & s,Thread * thread,lldb::addr_t base_addr) const490 void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {
491 if (!m_source_name.IsEmpty()) {
492 s.Printf("This UnwindPlan originally sourced from %s\n",
493 m_source_name.GetCString());
494 }
495 if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) {
496 TargetSP target_sp(thread->CalculateTarget());
497 addr_t lsda_load_addr = m_lsda_address.GetLoadAddress(target_sp.get());
498 addr_t personality_func_load_addr =
499 m_personality_func_addr.GetLoadAddress(target_sp.get());
500
501 if (lsda_load_addr != LLDB_INVALID_ADDRESS &&
502 personality_func_load_addr != LLDB_INVALID_ADDRESS) {
503 s.Printf("LSDA address 0x%" PRIx64
504 ", personality routine is at address 0x%" PRIx64 "\n",
505 lsda_load_addr, personality_func_load_addr);
506 }
507 }
508 s.Printf("This UnwindPlan is sourced from the compiler: ");
509 switch (m_plan_is_sourced_from_compiler) {
510 case eLazyBoolYes:
511 s.Printf("yes.\n");
512 break;
513 case eLazyBoolNo:
514 s.Printf("no.\n");
515 break;
516 case eLazyBoolCalculate:
517 s.Printf("not specified.\n");
518 break;
519 }
520 s.Printf("This UnwindPlan is valid at all instruction locations: ");
521 switch (m_plan_is_valid_at_all_instruction_locations) {
522 case eLazyBoolYes:
523 s.Printf("yes.\n");
524 break;
525 case eLazyBoolNo:
526 s.Printf("no.\n");
527 break;
528 case eLazyBoolCalculate:
529 s.Printf("not specified.\n");
530 break;
531 }
532 if (m_plan_valid_address_range.GetBaseAddress().IsValid() &&
533 m_plan_valid_address_range.GetByteSize() > 0) {
534 s.PutCString("Address range of this UnwindPlan: ");
535 TargetSP target_sp(thread->CalculateTarget());
536 m_plan_valid_address_range.Dump(&s, target_sp.get(),
537 Address::DumpStyleSectionNameOffset);
538 s.EOL();
539 }
540 collection::const_iterator pos, begin = m_row_list.begin(),
541 end = m_row_list.end();
542 for (pos = begin; pos != end; ++pos) {
543 s.Printf("row[%u]: ", (uint32_t)std::distance(begin, pos));
544 (*pos)->Dump(s, this, thread, base_addr);
545 }
546 }
547
SetSourceName(const char * source)548 void UnwindPlan::SetSourceName(const char *source) {
549 m_source_name = ConstString(source);
550 }
551
GetSourceName() const552 ConstString UnwindPlan::GetSourceName() const { return m_source_name; }
553
GetRegisterInfo(Thread * thread,uint32_t unwind_reg) const554 const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,
555 uint32_t unwind_reg) const {
556 if (thread) {
557 RegisterContext *reg_ctx = thread->GetRegisterContext().get();
558 if (reg_ctx) {
559 uint32_t reg;
560 if (m_register_kind == eRegisterKindLLDB)
561 reg = unwind_reg;
562 else
563 reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,
564 unwind_reg);
565 if (reg != LLDB_INVALID_REGNUM)
566 return reg_ctx->GetRegisterInfoAtIndex(reg);
567 }
568 }
569 return nullptr;
570 }
571