Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Symbol/UnwindPlan.cpp
39587 views
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/DebugInfo/DIContext.h"
19
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
20
#include <optional>
21
22
using namespace lldb;
23
using namespace lldb_private;
24
25
bool UnwindPlan::Row::RegisterLocation::
26
operator==(const UnwindPlan::Row::RegisterLocation &rhs) const {
27
if (m_type == rhs.m_type) {
28
switch (m_type) {
29
case unspecified:
30
case undefined:
31
case same:
32
return true;
33
34
case atCFAPlusOffset:
35
case isCFAPlusOffset:
36
case atAFAPlusOffset:
37
case isAFAPlusOffset:
38
return m_location.offset == rhs.m_location.offset;
39
40
case inOtherRegister:
41
return m_location.reg_num == rhs.m_location.reg_num;
42
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);
48
break;
49
}
50
}
51
return false;
52
}
53
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;
61
}
62
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;
70
}
71
72
static std::optional<std::pair<lldb::ByteOrder, uint32_t>>
73
GetByteOrderAndAddrSize(Thread *thread) {
74
if (!thread)
75
return std::nullopt;
76
ProcessSP process_sp = thread->GetProcess();
77
if (!process_sp)
78
return std::nullopt;
79
ArchSpec arch = process_sp->GetTarget().GetArchitecture();
80
return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());
81
}
82
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);
89
} else
90
s.PutCString("dwarf-expr");
91
}
92
93
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::ArrayRef(m_location.expr.opcodes, m_location.expr.length),
152
thread);
153
if (m_type == atDWARFExpression)
154
s.PutChar(']');
155
} break;
156
}
157
}
158
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::
169
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
192
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, llvm::ArrayRef(m_value.expr.opcodes, m_value.expr.length),
206
thread);
207
break;
208
case unspecified:
209
s.PutCString("unspecified");
210
break;
211
case isRaSearch:
212
s.Printf("RaSearch@SP%+d", m_value.ra_search_offset);
213
break;
214
}
215
}
216
217
void UnwindPlan::Row::Clear() {
218
m_cfa_value.SetUnspecified();
219
m_afa_value.SetUnspecified();
220
m_offset = 0;
221
m_unspecified_registers_are_undefined = false;
222
m_register_locations.clear();
223
}
224
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());
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
}
248
249
UnwindPlan::Row::Row() : m_cfa_value(), m_afa_value(), m_register_locations() {}
250
251
bool UnwindPlan::Row::GetRegisterInfo(
252
uint32_t reg_num,
253
UnwindPlan::Row::RegisterLocation &register_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;
257
return true;
258
}
259
if (m_unspecified_registers_are_undefined) {
260
register_location.SetUndefined();
261
return true;
262
}
263
return false;
264
}
265
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);
270
}
271
}
272
273
void UnwindPlan::Row::SetRegisterInfo(
274
uint32_t reg_num,
275
const UnwindPlan::Row::RegisterLocation register_location) {
276
m_register_locations[reg_num] = register_location;
277
}
278
279
bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,
280
int32_t offset,
281
bool can_replace) {
282
if (!can_replace &&
283
m_register_locations.find(reg_num) != m_register_locations.end())
284
return false;
285
RegisterLocation reg_loc;
286
reg_loc.SetAtCFAPlusOffset(offset);
287
m_register_locations[reg_num] = reg_loc;
288
return true;
289
}
290
291
bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,
292
int32_t offset,
293
bool can_replace) {
294
if (!can_replace &&
295
m_register_locations.find(reg_num) != m_register_locations.end())
296
return false;
297
RegisterLocation reg_loc;
298
reg_loc.SetIsCFAPlusOffset(offset);
299
m_register_locations[reg_num] = reg_loc;
300
return true;
301
}
302
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();
307
308
if (pos != end) {
309
if (!can_replace)
310
return false;
311
if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())
312
return false;
313
}
314
RegisterLocation reg_loc;
315
reg_loc.SetUndefined();
316
m_register_locations[reg_num] = reg_loc;
317
return true;
318
}
319
320
bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num,
321
bool can_replace) {
322
if (!can_replace &&
323
m_register_locations.find(reg_num) != m_register_locations.end())
324
return false;
325
RegisterLocation reg_loc;
326
reg_loc.SetUnspecified();
327
m_register_locations[reg_num] = reg_loc;
328
return true;
329
}
330
331
bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num,
332
uint32_t other_reg_num,
333
bool can_replace) {
334
if (!can_replace &&
335
m_register_locations.find(reg_num) != m_register_locations.end())
336
return false;
337
RegisterLocation reg_loc;
338
reg_loc.SetInRegister(other_reg_num);
339
m_register_locations[reg_num] = reg_loc;
340
return true;
341
}
342
343
bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,
344
bool must_replace) {
345
if (must_replace &&
346
m_register_locations.find(reg_num) == m_register_locations.end())
347
return false;
348
RegisterLocation reg_loc;
349
reg_loc.SetSame();
350
m_register_locations[reg_num] = reg_loc;
351
return true;
352
}
353
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;
360
}
361
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);
366
else
367
m_row_list.back() = row_sp;
368
}
369
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()) {
374
RowSP row = *it;
375
if (row->GetOffset() >= row_sp->GetOffset())
376
break;
377
it++;
378
}
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)
382
*it = row_sp;
383
}
384
385
UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const {
386
RowSP row;
387
if (!m_row_list.empty()) {
388
if (offset == -1)
389
row = m_row_list.back();
390
else {
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))
394
row = *pos;
395
else
396
break;
397
}
398
}
399
}
400
return row;
401
}
402
403
bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
404
return idx < m_row_list.size();
405
}
406
407
const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const {
408
if (idx < m_row_list.size())
409
return m_row_list[idx];
410
else {
411
Log *log = GetLog(LLDBLog::Unwind);
412
LLDB_LOGF(log,
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();
417
}
418
}
419
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();
425
}
426
return m_row_list.back();
427
}
428
429
int UnwindPlan::GetRowCount() const { return m_row_list.size(); }
430
431
void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) {
432
if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
433
m_plan_valid_address_range = range;
434
}
435
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);
440
if (log) {
441
StreamString s;
442
if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
443
LLDB_LOGF(log,
444
"UnwindPlan is invalid -- no unwind rows for UnwindPlan "
445
"'%s' at address %s",
446
m_source_name.GetCString(), s.GetData());
447
} else {
448
LLDB_LOGF(log,
449
"UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
450
m_source_name.GetCString());
451
}
452
}
453
return false;
454
}
455
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
458
// UnwindPlan.
459
if (GetRowAtIndex(0).get() == nullptr ||
460
GetRowAtIndex(0)->GetCFAValue().GetValueType() ==
461
Row::FAValue::unspecified) {
462
Log *log = GetLog(LLDBLog::Unwind);
463
if (log) {
464
StreamString s;
465
if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
466
LLDB_LOGF(log,
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());
470
} else {
471
LLDB_LOGF(log,
472
"UnwindPlan is invalid -- no CFA register defined in row 0 "
473
"for UnwindPlan '%s'",
474
m_source_name.GetCString());
475
}
476
}
477
return false;
478
}
479
480
if (!m_plan_valid_address_range.GetBaseAddress().IsValid() ||
481
m_plan_valid_address_range.GetByteSize() == 0)
482
return true;
483
484
if (!addr.IsValid())
485
return true;
486
487
if (m_plan_valid_address_range.ContainsFileAddress(addr))
488
return true;
489
490
return false;
491
}
492
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());
497
}
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());
503
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);
509
}
510
}
511
s.Printf("This UnwindPlan is sourced from the compiler: ");
512
switch (m_plan_is_sourced_from_compiler) {
513
case eLazyBoolYes:
514
s.Printf("yes.\n");
515
break;
516
case eLazyBoolNo:
517
s.Printf("no.\n");
518
break;
519
case eLazyBoolCalculate:
520
s.Printf("not specified.\n");
521
break;
522
}
523
s.Printf("This UnwindPlan is valid at all instruction locations: ");
524
switch (m_plan_is_valid_at_all_instruction_locations) {
525
case eLazyBoolYes:
526
s.Printf("yes.\n");
527
break;
528
case eLazyBoolNo:
529
s.Printf("no.\n");
530
break;
531
case eLazyBoolCalculate:
532
s.Printf("not specified.\n");
533
break;
534
}
535
s.Printf("This UnwindPlan is for a trap handler function: ");
536
switch (m_plan_is_for_signal_trap) {
537
case eLazyBoolYes:
538
s.Printf("yes.\n");
539
break;
540
case eLazyBoolNo:
541
s.Printf("no.\n");
542
break;
543
case eLazyBoolCalculate:
544
s.Printf("not specified.\n");
545
break;
546
}
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);
553
s.EOL();
554
}
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);
560
s.Printf("\n");
561
}
562
}
563
564
void UnwindPlan::SetSourceName(const char *source) {
565
m_source_name = ConstString(source);
566
}
567
568
ConstString UnwindPlan::GetSourceName() const { return m_source_name; }
569
570
const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,
571
uint32_t unwind_reg) const {
572
if (thread) {
573
RegisterContext *reg_ctx = thread->GetRegisterContext().get();
574
if (reg_ctx) {
575
uint32_t reg;
576
if (m_register_kind == eRegisterKindLLDB)
577
reg = unwind_reg;
578
else
579
reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,
580
unwind_reg);
581
if (reg != LLDB_INVALID_REGNUM)
582
return reg_ctx->GetRegisterInfoAtIndex(reg);
583
}
584
}
585
return nullptr;
586
}
587
588