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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of lldb/source/Symbol/UnwindPlan.cpp