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