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:: 23 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. 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. 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>> 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 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 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::makeArrayRef(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, 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 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 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 s.EOL(); 248 } 249 250 UnwindPlan::Row::Row() 251 : m_offset(0), m_cfa_value(), m_afa_value(), m_register_locations() {} 252 253 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 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 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 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 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 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 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 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 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 352 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 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 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 382 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 400 bool UnwindPlan::IsValidRowIndex(uint32_t idx) const { 401 return idx < m_row_list.size(); 402 } 403 404 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 417 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 426 int UnwindPlan::GetRowCount() const { return m_row_list.size(); } 427 428 void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) { 429 if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0) 430 m_plan_valid_address_range = range; 431 } 432 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 490 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 548 void UnwindPlan::SetSourceName(const char *source) { 549 m_source_name = ConstString(source); 550 } 551 552 ConstString UnwindPlan::GetSourceName() const { return m_source_name; } 553 554 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