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